From ecbf23ab734cebf1fc1a4d3ae4442b3c687a2a4e Mon Sep 17 00:00:00 2001 From: Stefano Franz Date: Wed, 7 Nov 2018 19:05:50 +0100 Subject: [PATCH] CORDA-2106: Print node info gen log on bootstrap failure (#4184) * print node-gen log when nodeInfo generation fails during bootstrapping * add logic to print out the legal name of the node which failed to generate nodeInfo --- .../internal/network/NetworkBootstrapper.kt | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt index ff94acacae..c3a7fd71e9 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkBootstrapper.kt @@ -1,6 +1,7 @@ package net.corda.nodeapi.internal.network import com.typesafe.config.Config +import com.typesafe.config.ConfigException import com.typesafe.config.ConfigFactory import net.corda.core.crypto.toStringShort import net.corda.core.identity.CordaX500Name @@ -30,6 +31,7 @@ import net.corda.serialization.internal.CordaSerializationMagic import net.corda.serialization.internal.SerializationFactoryImpl import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme import net.corda.serialization.internal.amqp.amqpMagic +import java.io.File import java.io.InputStream import java.nio.file.Path import java.nio.file.StandardCopyOption.REPLACE_EXISTING @@ -96,21 +98,36 @@ internal constructor(private val initSerEnv: Boolean, private fun generateNodeInfo(nodeDir: Path): Path { val logsDir = (nodeDir / LOGS_DIR_NAME).createDirectories() + val nodeInfoGenFile = (logsDir / "node-info-gen.log").toFile() val process = ProcessBuilder(nodeInfoGenCmd) .directory(nodeDir.toFile()) .redirectErrorStream(true) - .redirectOutput((logsDir / "node-info-gen.log").toFile()) + .redirectOutput(nodeInfoGenFile) .apply { environment()["CAPSULE_CACHE_DIR"] = "../.cache" } .start() if (!process.waitFor(3, TimeUnit.MINUTES)) { process.destroyForcibly() - throw IllegalStateException("Error while generating node info file. Please check the logs in $logsDir.") + printNodeInfoGenLogToConsole(nodeInfoGenFile) } - check(process.exitValue() == 0) { "Error while generating node info file. Please check the logs in $logsDir." } + printNodeInfoGenLogToConsole(nodeInfoGenFile) { process.exitValue() == 0 } return nodeDir.list { paths -> paths.filter { it.fileName.toString().startsWith(NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get() } } + + private fun printNodeInfoGenLogToConsole(nodeInfoGenFile: File, check: (() -> Boolean) = { true }) { + if (!check.invoke()) { + val nodeDir = nodeInfoGenFile.parent + val nodeIdentifier = try { + ConfigFactory.parseFile((nodeDir / "node.conf").toFile()).getString("myLegalName") + } catch (e: ConfigException) { + nodeDir + } + System.err.println("#### Error while generating node info file $nodeIdentifier ####") + nodeInfoGenFile.inputStream().copyTo(System.err) + throw IllegalStateException("Error while generating node info file. Please check the logs in $nodeDir.") + } + } } sealed class NotaryCluster { @@ -142,7 +159,7 @@ internal constructor(private val initSerEnv: Boolean, private fun isBFTNotary(config: Config): Boolean { // TODO: pass a commandline parameter to the bootstrapper instead. Better yet, a notary config map // specifying the notary identities and the type (single-node, CFT, BFT) of each notary to set up. - return config.getString ("notary.className").contains("BFT", true) + return config.getString("notary.className").contains("BFT", true) } private fun generateServiceIdentitiesForNotaryClusters(configs: Map) { @@ -172,7 +189,7 @@ internal constructor(private val initSerEnv: Boolean, } /** Entry point for the tool */ - fun bootstrap(directory: Path, copyCordapps: Boolean, minimumPlatformVersion: Int, packageOwnership : Map = emptyMap()) { + fun bootstrap(directory: Path, copyCordapps: Boolean, minimumPlatformVersion: Int, packageOwnership: Map = emptyMap()) { require(minimumPlatformVersion <= PLATFORM_VERSION) { "Minimum platform version cannot be greater than $PLATFORM_VERSION" } // Don't accidently include the bootstrapper jar as a CorDapp! val bootstrapperJar = javaClass.location.toPath() @@ -188,7 +205,7 @@ internal constructor(private val initSerEnv: Boolean, copyCordapps: Boolean, fromCordform: Boolean, minimumPlatformVersion: Int = PLATFORM_VERSION, - packageOwnership : Map = emptyMap() + packageOwnership: Map = emptyMap() ) { directory.createDirectories() println("Bootstrapping local test network in $directory") @@ -361,21 +378,20 @@ internal constructor(private val initSerEnv: Boolean, existingNetParams: NetworkParameters?, nodeDirs: List, minimumPlatformVersion: Int, - packageOwnership : Map + packageOwnership: Map ): NetworkParameters { // TODO Add config for maxMessageSize and maxTransactionSize val netParams = if (existingNetParams != null) { if (existingNetParams.whitelistedContractImplementations == whitelist && existingNetParams.notaries == notaryInfos && - existingNetParams.packageOwnership.entries.containsAll(packageOwnership.entries)) { + existingNetParams.packageOwnership.entries.containsAll(packageOwnership.entries)) { existingNetParams } else { - var updatePackageOwnership = mutableMapOf(*existingNetParams.packageOwnership.map { Pair(it.key,it.value) }.toTypedArray()) + var updatePackageOwnership = mutableMapOf(*existingNetParams.packageOwnership.map { Pair(it.key, it.value) }.toTypedArray()) packageOwnership.forEach { key, value -> if (value == null) { if (updatePackageOwnership.remove(key) != null) println("Unregistering package $key") - } - else { + } else { if (updatePackageOwnership.put(key, value) == null) println("Registering package $key for owner ${value.toStringShort()}") }