CORDA-892: Make cordform test use new network bootstrapper logic (#2307)

* Make cordform test use new network bootstrapper logic

* Fixing review comments

* Fix issue with backwards compatibility

* Fix issue with setup not being called from CordformDefinitions

* Make sure node dir is created (as CordformDefinition uses it directly if setup is overridden
Make sure tmp dir is created

* Don't crash if node dir is already created

* Stop overwriting errors
This commit is contained in:
Anthony Keenan
2018-01-05 09:21:59 +00:00
committed by GitHub
parent 00a02b30fe
commit 1661cea816
5 changed files with 85 additions and 72 deletions

View File

@ -1,4 +1,4 @@
gradlePluginsVersion=3.0.2 gradlePluginsVersion=3.0.3
kotlinVersion=1.1.60 kotlinVersion=1.1.60
platformVersion=2 platformVersion=2
guavaVersion=21.0 guavaVersion=21.0

View File

@ -22,6 +22,7 @@ import java.util.jar.JarInputStream
@Suppress("unused") @Suppress("unused")
open class Cordform : DefaultTask() { open class Cordform : DefaultTask() {
private companion object { private companion object {
val nodeJarName = "corda.jar"
private val defaultDirectory: Path = Paths.get("build", "nodes") private val defaultDirectory: Path = Paths.get("build", "nodes")
} }
@ -132,9 +133,26 @@ open class Cordform : DefaultTask() {
fun build() { fun build() {
project.logger.info("Running Cordform task") project.logger.info("Running Cordform task")
initializeConfiguration() initializeConfiguration()
nodes.forEach(Node::installConfig)
installCordaJar()
installRunScript() installRunScript()
nodes.forEach(Node::build)
bootstrapNetwork() bootstrapNetwork()
nodes.forEach(Node::build)
}
/**
* Installs the corda fat JAR to the root directory, for the network bootstrapper to use.
*/
private fun installCordaJar() {
val cordaJar = Cordformation.verifyAndGetRuntimeJar(project, "corda")
project.copy {
it.apply {
from(cordaJar)
into(directory)
rename(cordaJar.name, nodeJarName)
fileMode = Cordformation.executableFileMode
}
}
} }
private fun initializeConfiguration() { private fun initializeConfiguration() {
@ -150,8 +168,8 @@ open class Cordform : DefaultTask() {
cd.nodeConfigurers.forEach { cd.nodeConfigurers.forEach {
val node = node { } val node = node { }
it.accept(node) it.accept(node)
node.additionalCordapps.addAll(cordapps)
node.rootDir(directory) node.rootDir(directory)
node.installCordapps(cordapps)
} }
cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) } cd.setup { nodeName -> project.projectDir.toPath().resolve(getNodeByName(nodeName)!!.nodeDir.toPath()) }
} else { } else {

View File

@ -20,14 +20,35 @@ class Cordformation : Plugin<Project> {
* @return A file handle to the file in the JAR. * @return A file handle to the file in the JAR.
*/ */
fun getPluginFile(project: Project, filePathInJar: String): File { fun getPluginFile(project: Project, filePathInJar: String): File {
val archive: File? = project.rootProject.buildscript.configurations val archive = project.rootProject.buildscript.configurations
.single { it.name == "classpath" } .single { it.name == "classpath" }
.find { it.name.contains("cordformation") } .first { it.name.contains("cordformation") }
return project.rootProject.resources.text return project.rootProject.resources.text
.fromArchiveEntry(archive, filePathInJar) .fromArchiveEntry(archive, filePathInJar)
.asFile() .asFile()
} }
/**
* Gets a current built corda jar file
*
* @param project The project environment this plugin executes in.
* @param jarName The name of the JAR you wish to access.
* @return A file handle to the file in the JAR.
*/
fun verifyAndGetRuntimeJar(project: Project, jarName: String): File {
val releaseVersion = project.rootProject.ext<String>("corda_release_version")
val maybeJar = project.configuration("runtime").filter {
"$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString()
}
if (maybeJar.isEmpty) {
throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"")
} else {
val jar = maybeJar.singleFile
require(jar.isFile)
return jar
}
}
val executableFileMode = "0755".toInt(8) val executableFileMode = "0755".toInt(8)
} }

View File

@ -15,8 +15,6 @@ import java.nio.file.Path
*/ */
class Node(private val project: Project) : CordformNode() { class Node(private val project: Project) : CordformNode() {
companion object { companion object {
@JvmStatic
val nodeJarName = "corda.jar"
@JvmStatic @JvmStatic
val webJarName = "corda-webserver.jar" val webJarName = "corda-webserver.jar"
private val configFileProperty = "configFile" private val configFileProperty = "configFile"
@ -30,10 +28,11 @@ class Node(private val project: Project) : CordformNode() {
* @note Type is any due to gradle's use of "GStrings" - each value will have "toString" called on it * @note Type is any due to gradle's use of "GStrings" - each value will have "toString" called on it
*/ */
var cordapps = mutableListOf<Any>() var cordapps = mutableListOf<Any>()
var additionalCordapps = mutableListOf<File>()
private val releaseVersion = project.rootProject.ext<String>("corda_release_version")
internal lateinit var nodeDir: File internal lateinit var nodeDir: File
private set private set
internal lateinit var rootDir: File
private set
/** /**
* Sets whether this node will use HTTPS communication. * Sets whether this node will use HTTPS communication.
@ -65,16 +64,12 @@ class Node(private val project: Project) : CordformNode() {
} }
internal fun build() { internal fun build() {
configureProperties()
installCordaJar()
if (config.hasPath("webAddress")) { if (config.hasPath("webAddress")) {
installWebserverJar() installWebserverJar()
} }
installAgentJar() installAgentJar()
installBuiltCordapp() installBuiltCordapp()
installCordapps() installCordapps()
installConfig()
appendOptionalConfig()
} }
internal fun rootDir(rootDir: Path) { internal fun rootDir(rootDir: Path) {
@ -86,7 +81,9 @@ class Node(private val project: Project) : CordformNode() {
// with loading our custom X509EdDSAEngine. // with loading our custom X509EdDSAEngine.
val organizationName = name.trim().split(",").firstOrNull { it.startsWith("O=") }?.substringAfter("=") val organizationName = name.trim().split(",").firstOrNull { it.startsWith("O=") }?.substringAfter("=")
val dirName = organizationName ?: name val dirName = organizationName ?: name
nodeDir = File(rootDir.toFile(), dirName) this.rootDir = rootDir.toFile()
nodeDir = File(this.rootDir, dirName)
Files.createDirectories(nodeDir.toPath())
} }
private fun configureProperties() { private fun configureProperties() {
@ -99,26 +96,11 @@ class Node(private val project: Project) : CordformNode() {
} }
} }
/**
* Installs the corda fat JAR to the node directory.
*/
private fun installCordaJar() {
val cordaJar = verifyAndGetRuntimeJar("corda")
project.copy {
it.apply {
from(cordaJar)
into(nodeDir)
rename(cordaJar.name, nodeJarName)
fileMode = Cordformation.executableFileMode
}
}
}
/** /**
* Installs the corda webserver JAR to the node directory * Installs the corda webserver JAR to the node directory
*/ */
private fun installWebserverJar() { private fun installWebserverJar() {
val webJar = verifyAndGetRuntimeJar("corda-webserver") val webJar = Cordformation.verifyAndGetRuntimeJar(project, "corda-webserver")
project.copy { project.copy {
it.apply { it.apply {
from(webJar) from(webJar)
@ -141,19 +123,6 @@ class Node(private val project: Project) : CordformNode() {
} }
} }
/**
* Installs other cordapps to this node's cordapps directory.
*/
internal fun installCordapps(cordapps: Collection<File> = getCordappList()) {
val cordappsDir = File(nodeDir, "cordapps")
project.copy {
it.apply {
from(cordapps)
into(cordappsDir)
}
}
}
/** /**
* Installs the jolokia monitoring agent JAR to the node/drivers directory * Installs the jolokia monitoring agent JAR to the node/drivers directory
*/ */
@ -161,8 +130,8 @@ class Node(private val project: Project) : CordformNode() {
val jolokiaVersion = project.rootProject.ext<String>("jolokia_version") val jolokiaVersion = project.rootProject.ext<String>("jolokia_version")
val agentJar = project.configuration("runtime").files { val agentJar = project.configuration("runtime").files {
(it.group == "org.jolokia") && (it.group == "org.jolokia") &&
(it.name == "jolokia-jvm") && (it.name == "jolokia-jvm") &&
(it.version == jolokiaVersion) (it.version == jolokiaVersion)
// TODO: revisit when classifier attribute is added. eg && (it.classifier = "agent") // TODO: revisit when classifier attribute is added. eg && (it.classifier = "agent")
}.first() // should always be the jolokia agent fat jar: eg. jolokia-jvm-1.3.7-agent.jar }.first() // should always be the jolokia agent fat jar: eg. jolokia-jvm-1.3.7-agent.jar
project.logger.info("Jolokia agent jar: $agentJar") project.logger.info("Jolokia agent jar: $agentJar")
@ -177,10 +146,7 @@ class Node(private val project: Project) : CordformNode() {
} }
} }
/** private fun createTempConfigFile(): File {
* Installs the configuration file to this node's directory and detokenises it.
*/
private fun installConfig() {
val options = ConfigRenderOptions val options = ConfigRenderOptions
.defaults() .defaults()
.setOriginComments(false) .setOriginComments(false)
@ -188,16 +154,26 @@ class Node(private val project: Project) : CordformNode() {
.setFormatted(true) .setFormatted(true)
.setJson(false) .setJson(false)
val configFileText = config.root().render(options).split("\n").toList() val configFileText = config.root().render(options).split("\n").toList()
// Need to write a temporary file first to use the project.copy, which resolves directories correctly. // Need to write a temporary file first to use the project.copy, which resolves directories correctly.
val tmpDir = File(project.buildDir, "tmp") val tmpDir = File(project.buildDir, "tmp")
val tmpConfFile = File(tmpDir, "node.conf") Files.createDirectories(tmpDir.toPath())
var fileName = "${nodeDir.getName()}.conf"
val tmpConfFile = File(tmpDir, fileName)
Files.write(tmpConfFile.toPath(), configFileText, StandardCharsets.UTF_8) Files.write(tmpConfFile.toPath(), configFileText, StandardCharsets.UTF_8)
return tmpConfFile
}
/**
* Installs the configuration file to the root directory and detokenises it.
*/
internal fun installConfig() {
configureProperties()
val tmpConfFile = createTempConfigFile()
appendOptionalConfig(tmpConfFile)
project.copy { project.copy {
it.apply { it.apply {
from(tmpConfFile) from(tmpConfFile)
into(nodeDir) into(rootDir)
} }
} }
} }
@ -205,7 +181,7 @@ class Node(private val project: Project) : CordformNode() {
/** /**
* Appends installed config file with properties from an optional file. * Appends installed config file with properties from an optional file.
*/ */
private fun appendOptionalConfig() { private fun appendOptionalConfig(confFile: File) {
val optionalConfig: File? = when { val optionalConfig: File? = when {
project.findProperty(configFileProperty) != null -> //provided by -PconfigFile command line property when running Gradle task project.findProperty(configFileProperty) != null -> //provided by -PconfigFile command line property when running Gradle task
File(project.findProperty(configFileProperty) as String) File(project.findProperty(configFileProperty) as String)
@ -217,28 +193,22 @@ class Node(private val project: Project) : CordformNode() {
if (!optionalConfig.exists()) { if (!optionalConfig.exists()) {
project.logger.error("$configFileProperty '$optionalConfig' not found") project.logger.error("$configFileProperty '$optionalConfig' not found")
} else { } else {
val confFile = File(project.buildDir.path + "/../" + nodeDir, "node.conf")
confFile.appendBytes(optionalConfig.readBytes()) confFile.appendBytes(optionalConfig.readBytes())
} }
} }
} }
/** /**
* Find the given JAR amongst the dependencies * Installs other cordapps to this node's cordapps directory.
* @param jarName JAR name without the version part, for example for corda-2.0-SNAPSHOT.jar provide only "corda" as jarName
*
* @return A file representing found JAR
*/ */
private fun verifyAndGetRuntimeJar(jarName: String): File { internal fun installCordapps() {
val maybeJar = project.configuration("runtime").filter { additionalCordapps.addAll(getCordappList())
"$jarName-$releaseVersion.jar" in it.toString() || "$jarName-enterprise-$releaseVersion.jar" in it.toString() val cordappsDir = File(nodeDir, "cordapps")
} project.copy {
if (maybeJar.isEmpty) { it.apply {
throw IllegalStateException("No $jarName JAR found. Have you deployed the Corda project to Maven? Looked for \"$jarName-$releaseVersion.jar\"") from(additionalCordapps)
} else { into(cordappsDir)
val jar = maybeJar.singleFile }
require(jar.isFile)
return jar
} }
} }

View File

@ -83,16 +83,20 @@ class NetworkBootstrapper {
for (confFile in confFiles) { for (confFile in confFiles) {
val nodeName = confFile.fileName.toString().removeSuffix(".conf") val nodeName = confFile.fileName.toString().removeSuffix(".conf")
println("Generating directory for $nodeName") println("Generating directory for $nodeName")
val nodeDir = (directory / nodeName).createDirectory() val nodeDir = (directory / nodeName)
confFile.moveTo(nodeDir / "node.conf") if (!nodeDir.exists()) { nodeDir.createDirectory() }
Files.copy(cordaJar, (nodeDir / "corda.jar")) confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING)
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING)
} }
Files.delete(cordaJar) Files.delete(cordaJar)
} }
private fun extractCordaJarTo(directory: Path): Path { private fun extractCordaJarTo(directory: Path): Path {
val cordaJarPath = (directory / "corda.jar") val cordaJarPath = (directory / "corda.jar")
Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath) if (!cordaJarPath.exists()) {
println("No corda jar found in root directory. Extracting from jar")
Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar").copyTo(cordaJarPath)
}
return cordaJarPath return cordaJarPath
} }