diff --git a/applications/p2p-gateway/build.gradle b/applications/p2p-gateway/build.gradle index e89956f5aac..6c07bd9e36b 100644 --- a/applications/p2p-gateway/build.gradle +++ b/applications/p2p-gateway/build.gradle @@ -1,6 +1,7 @@ plugins { id 'corda.common-publishing' id 'corda.common-app' + id 'corda.docker-app' } description 'Gateway Application' diff --git a/applications/p2p-link-manager/build.gradle b/applications/p2p-link-manager/build.gradle index 4d15549579d..8c4376f9741 100644 --- a/applications/p2p-link-manager/build.gradle +++ b/applications/p2p-link-manager/build.gradle @@ -1,6 +1,7 @@ plugins { id 'corda.common-publishing' id 'corda.common-app' + id 'corda.docker-app' } description 'P2P Link Manager Application' @@ -53,5 +54,5 @@ dependencies { implementation project(":libs:messaging:messaging") implementation project(":components:configuration:configuration-read-service") - image "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" + dockerImage "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" } diff --git a/applications/tools/p2p-test/app-simulator/build.gradle b/applications/tools/p2p-test/app-simulator/build.gradle index 1fc1ddc1cbb..4908dc47479 100644 --- a/applications/tools/p2p-test/app-simulator/build.gradle +++ b/applications/tools/p2p-test/app-simulator/build.gradle @@ -1,6 +1,7 @@ plugins { id 'corda.common-publishing' id 'corda.common-app' + id 'corda.docker-app' } description "P2P Testing tools - Application-level simulator" diff --git a/applications/workers/release/combined-worker/build.gradle b/applications/workers/release/combined-worker/build.gradle index fbf74c24a30..f0366bff356 100644 --- a/applications/workers/release/combined-worker/build.gradle +++ b/applications/workers/release/combined-worker/build.gradle @@ -1,6 +1,7 @@ plugins { id 'corda.common-publishing' id 'corda.quasar-app' + id 'corda.docker-app' } description 'Combined Worker' diff --git a/applications/workers/release/crypto-worker/build.gradle b/applications/workers/release/crypto-worker/build.gradle index a1f5a5c910f..7408ce4f774 100644 --- a/applications/workers/release/crypto-worker/build.gradle +++ b/applications/workers/release/crypto-worker/build.gradle @@ -1,6 +1,7 @@ plugins { id 'corda.common-publishing' id 'corda.common-app' + id 'corda.docker-app' } description 'Crypto Worker' @@ -30,5 +31,5 @@ dependencies { testRuntimeOnly "org.apache.felix:org.apache.felix.framework:$felixVersion" testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion" - image "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" + dockerImage "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" } diff --git a/applications/workers/release/db-worker/build.gradle b/applications/workers/release/db-worker/build.gradle index ef3bb0bb09e..3d04391f28a 100644 --- a/applications/workers/release/db-worker/build.gradle +++ b/applications/workers/release/db-worker/build.gradle @@ -1,6 +1,7 @@ plugins { id 'corda.common-publishing' id 'corda.common-app' + id 'corda.docker-app' } description 'DB Worker' @@ -41,5 +42,5 @@ dependencies { exclude group: 'org.osgi' } - image "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" + dockerImage "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" } diff --git a/applications/workers/release/flow-worker/build.gradle b/applications/workers/release/flow-worker/build.gradle index 82f6749ad0e..d7ccfec6c37 100644 --- a/applications/workers/release/flow-worker/build.gradle +++ b/applications/workers/release/flow-worker/build.gradle @@ -1,6 +1,7 @@ plugins { id 'corda.common-publishing' id 'corda.quasar-app' + id 'corda.docker-app' } description 'Flow Worker' @@ -41,5 +42,5 @@ dependencies { exclude group: 'org.osgi' } - image "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" + dockerImage "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" } diff --git a/applications/workers/release/member-worker/build.gradle b/applications/workers/release/member-worker/build.gradle index 19500027744..3f8264953df 100644 --- a/applications/workers/release/member-worker/build.gradle +++ b/applications/workers/release/member-worker/build.gradle @@ -1,6 +1,7 @@ plugins { id 'corda.common-publishing' id 'corda.common-app' + id 'corda.docker-app' } description 'Member Worker' @@ -25,5 +26,5 @@ dependencies { runtimeOnly "org.apache.felix:org.apache.felix.configadmin:$felixConfigAdminVersion" runtimeOnly project(":libs:messaging:kafka-message-bus-impl") - image "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" + dockerImage "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" } diff --git a/applications/workers/release/rpc-worker/build.gradle b/applications/workers/release/rpc-worker/build.gradle index 446a4df3336..a601fa277d1 100644 --- a/applications/workers/release/rpc-worker/build.gradle +++ b/applications/workers/release/rpc-worker/build.gradle @@ -1,6 +1,7 @@ plugins { id 'corda.common-publishing' id 'corda.common-app' + id 'corda.docker-app' } description 'RPC Worker' @@ -56,7 +57,7 @@ dependencies { e2eTestImplementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion" e2eTestImplementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion" - image "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" + dockerImage "io.opentelemetry.javaagent:opentelemetry-javaagent:$openTelemetryVersion" } tasks.register('e2eTest', Test) { diff --git a/build.gradle b/build.gradle index 7e21a5b59a1..e9e8fdabcb4 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { } plugins { - id 'org.jetbrains.kotlin.jvm' + id 'org.jetbrains.kotlin.jvm' apply false id 'org.jetbrains.kotlin.plugin.allopen' apply false id 'org.jetbrains.kotlin.plugin.jpa' apply false id 'io.gitlab.arturbosch.detekt' apply false @@ -137,19 +137,31 @@ allprojects { //needed for C5 binaries maven { url = "$artifactoryContextUrl/corda-os-maven" + authentication { + basic(BasicAuthentication) + } credentials { username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME') password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD') } } - // needed for plugin-host dependencies maven { - url = "$artifactoryContextUrl/engineering-tools-maven-unstable-local" + url = "$artifactoryContextUrl/engineering-tools-maven" + authentication { + basic(BasicAuthentication) + } credentials { username = findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME') password = findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD') } + content { + // YourKit Java Profiler agent artifact. + // To publish agent locally: + // $ cd profiler + // $ ../gradlew publishToMavenLocal + includeGroup 'com.yourkit.corda' + } } def cordaUseCache = System.getenv("CORDA_USE_CACHE") @@ -346,8 +358,6 @@ subprojects { task allDependencies(type: DependencyReportTask) {} configurations { - // configuration used by DeployableContainerBuilder to add dependencies to Docker images - image { transitive = false } all { resolutionStrategy { // FORCE Gradle to use latest dynamic versions. @@ -371,43 +381,6 @@ subprojects { "javax.persistence.MappedSuperclass" ) } - - // apply docker publishing to workers and select testing applications - def nonWorkerImages = ['p2p-gateway', 'p2p-link-manager', 'app-simulator', 'configuration-publisher'] - if (it.toString().contains(":applications:workers:release:") || nonWorkerImages.contains(it.name)) { - - tasks.register('publishOSGiImage', DeployableContainerBuilder) { - // Bit of a hack to ensure we always get a flat list of tasks - def sourceTasks = (List)[project.tasks.named("appJar").get()].flatten() - dependsOn(sourceTasks) - it.sourceTasks = sourceTasks - it.useShortName = true - if (project.hasProperty('jibRemotePublish')) { - remotePublish = jibRemotePublish.toBoolean() - } - - if (project.hasProperty('isReleaseCandidate')) { - releaseCandidate = isReleaseCandidate.toBoolean() - } - - if (project.hasProperty('isNightly')) { - nightlyBuild = isNightly.toBoolean() - } - - if (project.hasProperty('isPreTest')) { - preTest = isPreTest.toBoolean() - } - - if (project.hasProperty('baseImage')) { - baseImageName = baseImage - } - - if(project.hasProperty('useDockerDaemon')){ - useDaemon = useDockerDaemon.toBoolean() - } - } - - } } // report updatable dependencies: gradle dependencyUpdates @@ -416,6 +389,7 @@ def isNonStable = { String version -> def regex = /^[0-9,.v-]+(-r)?$/ return !stableKeyword && !(version ==~ regex) } + tasks.named("dependencyUpdates").configure { rejectVersionIf { isNonStable(it.candidate.version) && !isNonStable(it.currentVersion) diff --git a/buildSrc/src/main/groovy/DeployableContainerBuilder.groovy b/buildSrc/src/main/groovy/DeployableContainerBuilder.groovy index b89e9568d21..4898694db53 100644 --- a/buildSrc/src/main/groovy/DeployableContainerBuilder.groovy +++ b/buildSrc/src/main/groovy/DeployableContainerBuilder.groovy @@ -2,13 +2,21 @@ import com.google.cloud.tools.jib.api.* import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath import com.google.cloud.tools.jib.api.buildplan.Platform import org.gradle.api.DefaultTask -import org.gradle.api.Task +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property import org.gradle.api.provider.ProviderFactory -import org.gradle.api.tasks.* +import org.gradle.api.tasks.Exec +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.SkipWhenEmpty +import org.gradle.api.tasks.TaskAction +import static org.gradle.api.tasks.PathSensitivity.RELATIVE import javax.inject.Inject import java.nio.file.Files @@ -101,9 +109,16 @@ abstract class DeployableContainerBuilder extends DefaultTask { final ListProperty arguments = getObjects().listProperty(String) - @Input - final ListProperty sourceTasks = - getObjects().listProperty(Task) + @PathSensitive(RELATIVE) + @SkipWhenEmpty + @InputFiles + final ConfigurableFileCollection sourceFiles = + getObjects().fileCollection() + + @PathSensitive(RELATIVE) + @InputFiles + final ConfigurableFileCollection extraSourceFiles = + getObjects().fileCollection() @Input final Property overrideEntryName = @@ -135,7 +150,6 @@ abstract class DeployableContainerBuilder extends DefaultTask { @TaskAction def updateImage() { - def outputFiles = sourceTasks.get().collect{ it -> it.getOutputs().files.files }.flatten() as List def buildBaseDir = temporaryDir.toPath() def containerizationDir = Paths.get("$buildBaseDir/containerization/") @@ -148,7 +162,7 @@ abstract class DeployableContainerBuilder extends DefaultTask { Files.createDirectories(containerizationDir) } - outputFiles.forEach{ + sourceFiles.forEach{ def jarName = useShortName ? it.name.replace("corda-", "").replace("-${project.version}", "") : it.name @@ -175,7 +189,7 @@ abstract class DeployableContainerBuilder extends DefaultTask { logger.info("Resolving base image ${baseImageName.get()}: ${baseImageTag.get()} from remote repo") builder = setCredentialsOnBaseImage(builder) } - List imageFiles = project.configurations.getByName("image").collect { it.toPath() } + List imageFiles = extraSourceFiles.collect { it.toPath() } if (!imageFiles.empty) { builder.addLayer(imageFiles, CONTAINER_LOCATION) } diff --git a/buildSrc/src/main/groovy/corda.docker-app.gradle b/buildSrc/src/main/groovy/corda.docker-app.gradle new file mode 100644 index 00000000000..6cd71fcf547 --- /dev/null +++ b/buildSrc/src/main/groovy/corda.docker-app.gradle @@ -0,0 +1,53 @@ +plugins { + id 'corda.common-app' +} + +configurations { + dockerImage { + canBeConsumed = false + transitive = false + } + + profilerAgent { + canBeConsumed = false + visible = false + } +} + +if (enableProfiling.toBoolean()) { + dependencies { + // Unzip the Java profiler into the Docker image. + profilerAgent "com.yourkit.corda:yourkit-agent-linux-x86-64:$profilerVersion@zip" + dockerImage provider { zipTree(configurations.profilerAgent.singleFile) } + } +} + +tasks.register('publishOSGiImage', DeployableContainerBuilder) { + it.sourceFiles.setFrom(tasks.named('appJar', Jar)) + it.extraSourceFiles.setFrom(configurations.dockerImage) + it.useShortName = true + + if (project.hasProperty('jibRemotePublish')) { + remotePublish = jibRemotePublish.toBoolean() + } + + if (project.hasProperty('isReleaseCandidate')) { + releaseCandidate = isReleaseCandidate.toBoolean() + } + + if (project.hasProperty('isNightly')) { + nightlyBuild = isNightly.toBoolean() + } + + if (project.hasProperty('isPreTest')) { + preTest = isPreTest.toBoolean() + } + + if (project.hasProperty('baseImage')) { + baseImageName = baseImage + } + + if (project.hasProperty('useDockerDaemon')) { + useDaemon = useDockerDaemon.toBoolean() + } +} diff --git a/charts/corda/templates/_helpers.tpl b/charts/corda/templates/_helpers.tpl index 0b130fcbc4f..32e848d40d3 100644 --- a/charts/corda/templates/_helpers.tpl +++ b/charts/corda/templates/_helpers.tpl @@ -163,6 +163,9 @@ Worker environment variables value: {{- if ( get .Values.workers .worker ).debug.enabled }} -agentlib:jdwp=transport=dt_socket,server=y,address=5005,suspend={{ if ( get .Values.workers .worker ).debug.suspend }}y{{ else }}n{{ end }} {{- end -}} + {{- if ( get .Values.workers .worker ).profiling.enabled }} + -agentpath:/opt/override/libyjpagent.so=exceptions=disable,port=10045,listen=all + {{- end -}} {{- if ( get .Values.workers .worker ).verifyInstrumentation }} -Dco.paralleluniverse.fibers.verifyInstrumentation=true {{- end -}} diff --git a/charts/corda/values.yaml b/charts/corda/values.yaml index d6bf039fd1d..b3ecf7cb3d3 100644 --- a/charts/corda/values.yaml +++ b/charts/corda/values.yaml @@ -142,6 +142,9 @@ workers: enabled: false # -- if debug is enabled, suspend the crypto worker until the debugger is attached suspend: false + profiling: + # -- run crypto worker with profiling enabled + enabled: false # resource limits and requests configuration for the crypto worker containers resources: # -- the CPU/memory resource requests for the crypto worker containers @@ -167,6 +170,9 @@ workers: enabled: false # -- if debug is enabled, suspend the DB worker until the debugger is attached suspend: false + profiling: + # -- run DB worker with profiling enabled + enabled: false # -- DB worker salt, defaults to a value randomly-generated on install salt: "" # -- DB worker passphrase, defaults to a value randomly-generated on install @@ -196,6 +202,9 @@ workers: enabled: false # -- if debug is enabled, suspend the flow worker until the debugger is attached suspend: false + profiling: + # -- run flow worker with profiling enabled + enabled: false # resource limits and requests configuration for the flow worker containers. resources: # -- the CPU/memory resource requests for the flow worker containers @@ -223,6 +232,9 @@ workers: enabled: false # -- if debug is enabled, suspend the membership worker until the debugger is attached suspend: false + profiling: + # -- run membership worker with profiling enabled + enabled: false # resource limits and requests configuration for the membership worker containers resources: # -- the CPU/memory resource requests for the membership worker containers @@ -248,6 +260,9 @@ workers: enabled: false # -- if debug is enabled, suspend the RPC worker until the debugger is attached suspend: false + profiling: + # -- run RPC worker with profiling enabled + enabled: false # -- resource limits and requests configuration for the RPC worker containers. resources: # -- the CPU/memory resource requests for the RPC worker containers @@ -283,9 +298,12 @@ workers: enabled: false # -- if debug is enabled, suspend the p2p-link-manager worker until the debugger is attached suspend: false + profiling: + # -- run p2p-link-manager worker with profiling enabled + enabled: false # resource limits and requests configuration for the p2p-link-manager worker containers. resources: # -- the CPU/memory resource requests for the p2p-link-manager worker containers requests: {} # -- the CPU/memory resource limits for the p2p-link-manager worker containers - limits: {} \ No newline at end of file + limits: {} diff --git a/gradle.properties b/gradle.properties index 300e87d492c..633b95195a8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -135,6 +135,10 @@ postgresDb= postgresUser= postgresPassword= +# Profiling support +enableProfiling=false +profilerVersion=2022.3 + # Kotlin build kotlin.build.report.output=file,build_scan diff --git a/profiler/README.md b/profiler/README.md new file mode 100644 index 00000000000..01a46275ad2 --- /dev/null +++ b/profiler/README.md @@ -0,0 +1,7 @@ +# YourKit Java Profiler Agent + +This small standalone project repackages YourKit's Linux x86_64 Java Profiler +agent for publishing into Artifactory. + +The goal is optionally to include this agent in Corda's Docker images, +so that developers can then profile their applications. diff --git a/profiler/build.gradle b/profiler/build.gradle new file mode 100644 index 00000000000..4da648b4ce7 --- /dev/null +++ b/profiler/build.gradle @@ -0,0 +1,79 @@ +plugins { + id 'com.jfrog.artifactory' + id 'maven-publish' + id 'base' +} + +description 'Creates a ZIP artifact containing the YourKit Java Profiler agent for Linux x86_64' + +def downloadUrl = "${downloadBaseUrl}/${yourkitVersion}/YourKit-JavaProfiler-${yourkitVersion}-b103.zip" +def downloadDir = layout.buildDirectory.dir('download') +def yourkitProfilerZip = objects.fileProperty().value(downloadDir.map { it.file('yourkit.zip') }) +def distributionDir = layout.buildDirectory.dir('distribution') + +def downloadZip = tasks.register('downloadZip') { + inputs.property('downloadUrl', downloadUrl) + outputs.file yourkitProfilerZip + + doLast { + ant.get(src: downloadUrl, dest: yourkitProfilerZip.get(), retries: 3, verbose: 'on') + } +} + +def extractAgent = tasks.register('extractAgent', Zip) { + inputs.file yourkitProfilerZip + dependsOn downloadZip + + destinationDirectory = distributionDir + archiveBaseName = 'yourkit' + archiveAppendix = 'linux-x86-64' + archiveVersion = yourkitVersion + + from(zipTree(yourkitProfilerZip)) { + include '*/license-redist.txt' + include '*/bin/linux-x86-64/libyjpagent.so' + } + reproducibleFileOrder = true +} + +artifacts { + archives extractAgent +} + +publishing { + publications { + yourkitAgent(MavenPublication) { + groupId 'com.yourkit.corda' + artifactId 'yourkit-agent-linux-x86-64' + version yourkitVersion + + artifact extractAgent + pom { + name = 'YourKit Java Profiler agent for Linux x86_64' + url = 'https://www.yourkit.com' + + licenses { + license { + name = 'YourKit (redistribution)' + distribution = 'repo' + } + } + } + } + } +} + +artifactory { + publish { + contextUrl = artifactoryContextUrl + repository { + repoKey = 'engineering-tools-maven-stable' + username = project.findProperty('cordaArtifactoryUsername') ?: System.getenv('CORDA_ARTIFACTORY_USERNAME') + password = project.findProperty('cordaArtifactoryPassword') ?: System.getenv('CORDA_ARTIFACTORY_PASSWORD') + } + + defaults { + publications 'yourkitAgent' + } + } +} diff --git a/profiler/gradle.properties b/profiler/gradle.properties new file mode 100644 index 00000000000..b5581307124 --- /dev/null +++ b/profiler/gradle.properties @@ -0,0 +1,5 @@ +artifactoryContextUrl=https://software.r3.com/artifactory +artifactoryPluginVersion=4.28.2 + +downloadBaseUrl=https://download.yourkit.com/yjp/ +yourkitVersion=2022.3 diff --git a/profiler/settings.gradle b/profiler/settings.gradle new file mode 100644 index 00000000000..c7d093ec224 --- /dev/null +++ b/profiler/settings.gradle @@ -0,0 +1,13 @@ +pluginManagement { + plugins { + id 'com.jfrog.artifactory' version artifactoryPluginVersion + } +} + +rootProject.name = 'yourkit-profiler' + +dependencyResolutionManagement { + repositories { + mavenCentral() + } +} diff --git a/tools/plugins/build.gradle b/tools/plugins/build.gradle index cde31838117..ae9fe0e406e 100644 --- a/tools/plugins/build.gradle +++ b/tools/plugins/build.gradle @@ -4,13 +4,12 @@ group 'net.corda.cli.deployment' tasks.register('publishOSGiImage', DeployableContainerBuilder) { def sourceTasks = subprojects.collect { it.tasks.named("cliPluginTask").get() } def cordaCliIncluded = gradle.includedBuilds.collect { it.name == "corda-cli-plugin-host" }.contains(true) - dependsOn(sourceTasks) if (cordaCliIncluded) { logger.lifecycle("corda-cli-plugin-host project detected in composite build logic, building base image from include project") dependsOn gradle.includedBuild("corda-cli-plugin-host").task(':app:publishOSGiImage') } - it.sourceTasks = sourceTasks + it.sourceFiles.setFrom(sourceTasks) it.subDir = "plugins/" it.setEntry = false if (project.hasProperty('jibRemotePublish')) {