mirror of
https://github.com/corda/corda.git
synced 2025-02-07 11:30:22 +00:00
NOTICK: Quick and dirty change to stop "Unable to start notaries." error message (#5686)
* Quick and dirty change to stop "Unable to start notaries." error message (#5686) "Unable to start notaries. A required port might be bound already" is returned whenever a startup error occurs while starting the notary nodes in driver tests. This hides the real error. This change prints the actual error to std_err and read from file at a later point. This means the real error is not lost and will be shown in failed builds. * Suppress detekt warnings
This commit is contained in:
parent
b1e5b659c1
commit
b15db200e3
@ -35,6 +35,7 @@ import org.slf4j.bridge.SLF4JBridgeHandler
|
|||||||
import picocli.CommandLine.Mixin
|
import picocli.CommandLine.Mixin
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.RandomAccessFile
|
import java.io.RandomAccessFile
|
||||||
|
import java.lang.NullPointerException
|
||||||
import java.lang.management.ManagementFactory
|
import java.lang.management.ManagementFactory
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.nio.channels.UnresolvedAddressException
|
import java.nio.channels.UnresolvedAddressException
|
||||||
@ -440,13 +441,31 @@ interface NodeStartupLogging {
|
|||||||
companion object {
|
companion object {
|
||||||
val logger by lazy { contextLogger() }
|
val logger by lazy { contextLogger() }
|
||||||
val startupErrors = setOf(MultipleCordappsForFlowException::class, CheckpointIncompatibleException::class, AddressBindingException::class, NetworkParametersReader::class, DatabaseIncompatibleException::class)
|
val startupErrors = setOf(MultipleCordappsForFlowException::class, CheckpointIncompatibleException::class, AddressBindingException::class, NetworkParametersReader::class, DatabaseIncompatibleException::class)
|
||||||
|
@Suppress("TooGenericExceptionCaught")
|
||||||
|
val PRINT_ERRORS_TO_STD_ERR = try {
|
||||||
|
System.getProperty("net.corda.node.printErrorsToStdErr") == "true"
|
||||||
|
} catch (e: NullPointerException) {
|
||||||
|
false
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <RESULT> attempt(action: () -> RESULT): Try<RESULT> = Try.on(action).throwError()
|
fun <RESULT> attempt(action: () -> RESULT): Try<RESULT> = Try.on(action).throwError()
|
||||||
|
|
||||||
fun Throwable.logAsExpected(message: String? = this.message, print: (String?) -> Unit = logger::error) = print(message)
|
fun Throwable.logAsExpected(message: String? = this.message, print: (String?) -> Unit = logger::error) = print(message)
|
||||||
|
|
||||||
fun Throwable.logAsUnexpected(message: String? = this.message, error: Throwable = this, print: (String?, Throwable) -> Unit = logger::error) {
|
fun Throwable.logAsUnexpected(
|
||||||
|
message: String? = this.message,
|
||||||
|
error: Throwable = this,
|
||||||
|
print: (String?, Throwable) -> Unit = { s, e ->
|
||||||
|
logger.error(s, e)
|
||||||
|
if (PRINT_ERRORS_TO_STD_ERR) {
|
||||||
|
System.err.println(s)
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
print("$message${this.message?.let { ": $it" } ?: ""}", error)
|
print("$message${this.message?.let { ": $it" } ?: ""}", error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,12 +58,12 @@ import rx.schedulers.Schedulers
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.ConnectException
|
import java.net.ConnectException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.ZoneOffset.UTC
|
import java.time.ZoneOffset.UTC
|
||||||
|
import java.time.ZonedDateTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
@ -377,6 +377,13 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
_notaries.map { notary -> notary.map { handle -> handle.nodeHandles } }.getOrThrow(notaryHandleTimeout).forEach { future -> future.getOrThrow(notaryHandleTimeout) }
|
_notaries.map { notary -> notary.map { handle -> handle.nodeHandles } }.getOrThrow(notaryHandleTimeout).forEach { future -> future.getOrThrow(notaryHandleTimeout) }
|
||||||
|
} catch(e: NodeListenProcessDeathException) {
|
||||||
|
val message = if (e.causeFromStdError.isNotBlank()) {
|
||||||
|
"Unable to start notaries. Failed with the following error: ${e.causeFromStdError}"
|
||||||
|
} else {
|
||||||
|
"Unable to start notaries. A required port might be bound already."
|
||||||
|
}
|
||||||
|
throw IllegalStateException(message)
|
||||||
} catch (e: ListenProcessDeathException) {
|
} catch (e: ListenProcessDeathException) {
|
||||||
throw IllegalStateException("Unable to start notaries. A required port might be bound already.", e)
|
throw IllegalStateException("Unable to start notaries. A required port might be bound already.", e)
|
||||||
} catch (e: TimeoutException) {
|
} catch (e: TimeoutException) {
|
||||||
@ -550,6 +557,7 @@ class DriverDSLImpl(
|
|||||||
* Start the node with the given flag which is expected to start the node for some function, which once complete will
|
* Start the node with the given flag which is expected to start the node for some function, which once complete will
|
||||||
* terminate the node.
|
* terminate the node.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("SpreadOperator")
|
||||||
private fun startOutOfProcessMiniNode(config: NodeConfig, vararg extraCmdLineFlag: String): CordaFuture<Unit> {
|
private fun startOutOfProcessMiniNode(config: NodeConfig, vararg extraCmdLineFlag: String): CordaFuture<Unit> {
|
||||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||||
val process = startOutOfProcessNode(
|
val process = startOutOfProcessNode(
|
||||||
@ -561,6 +569,7 @@ class DriverDSLImpl(
|
|||||||
systemProperties,
|
systemProperties,
|
||||||
"512m",
|
"512m",
|
||||||
null,
|
null,
|
||||||
|
ZonedDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss.SSS")),
|
||||||
*extraCmdLineFlag
|
*extraCmdLineFlag
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -577,6 +586,7 @@ class DriverDSLImpl(
|
|||||||
bytemanPort: Int?): CordaFuture<NodeHandle> {
|
bytemanPort: Int?): CordaFuture<NodeHandle> {
|
||||||
val visibilityHandle = networkVisibilityController.register(config.corda.myLegalName)
|
val visibilityHandle = networkVisibilityController.register(config.corda.myLegalName)
|
||||||
val baseDirectory = config.corda.baseDirectory.createDirectories()
|
val baseDirectory = config.corda.baseDirectory.createDirectories()
|
||||||
|
val identifier = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss.SSS"))
|
||||||
localNetworkMap?.networkParametersCopier?.install(baseDirectory)
|
localNetworkMap?.networkParametersCopier?.install(baseDirectory)
|
||||||
localNetworkMap?.nodeInfosCopier?.addConfig(baseDirectory)
|
localNetworkMap?.nodeInfosCopier?.addConfig(baseDirectory)
|
||||||
|
|
||||||
@ -628,7 +638,8 @@ class DriverDSLImpl(
|
|||||||
bytemanPort,
|
bytemanPort,
|
||||||
systemProperties,
|
systemProperties,
|
||||||
parameters.maximumHeapSize,
|
parameters.maximumHeapSize,
|
||||||
parameters.logLevelOverride
|
parameters.logLevelOverride,
|
||||||
|
identifier
|
||||||
)
|
)
|
||||||
|
|
||||||
// Destroy the child process when the parent exits.This is needed even when `waitForAllNodesToFinish` is
|
// Destroy the child process when the parent exits.This is needed even when `waitForAllNodesToFinish` is
|
||||||
@ -646,7 +657,18 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val effectiveP2PAddress = config.corda.messagingServerAddress ?: config.corda.p2pAddress
|
val effectiveP2PAddress = config.corda.messagingServerAddress ?: config.corda.p2pAddress
|
||||||
val p2pReadyFuture = addressMustBeBoundFuture(executorService, effectiveP2PAddress, process)
|
val p2pReadyFuture = nodeMustBeStartedFuture(
|
||||||
|
executorService,
|
||||||
|
effectiveP2PAddress,
|
||||||
|
process
|
||||||
|
) {
|
||||||
|
NodeListenProcessDeathException(
|
||||||
|
effectiveP2PAddress,
|
||||||
|
process,
|
||||||
|
(config.corda.baseDirectory / "net.corda.node.Corda.$identifier.stderr.log").readText()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
p2pReadyFuture.flatMap {
|
p2pReadyFuture.flatMap {
|
||||||
val processDeathFuture = poll(executorService, "process death while waiting for RPC (${config.corda.myLegalName})") {
|
val processDeathFuture = poll(executorService, "process death while waiting for RPC (${config.corda.myLegalName})") {
|
||||||
if (process.isAlive) null else process
|
if (process.isAlive) null else process
|
||||||
@ -753,7 +775,7 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("ComplexMethod", "MaxLineLength")
|
@Suppress("ComplexMethod", "MaxLineLength", "LongParameterList")
|
||||||
private fun startOutOfProcessNode(
|
private fun startOutOfProcessNode(
|
||||||
config: NodeConfig,
|
config: NodeConfig,
|
||||||
quasarJarPath: String,
|
quasarJarPath: String,
|
||||||
@ -763,6 +785,7 @@ class DriverDSLImpl(
|
|||||||
overriddenSystemProperties: Map<String, String>,
|
overriddenSystemProperties: Map<String, String>,
|
||||||
maximumHeapSize: String,
|
maximumHeapSize: String,
|
||||||
logLevelOverride: String?,
|
logLevelOverride: String?,
|
||||||
|
identifier: String,
|
||||||
vararg extraCmdLineFlag: String
|
vararg extraCmdLineFlag: String
|
||||||
): Process {
|
): Process {
|
||||||
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " +
|
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " +
|
||||||
@ -835,10 +858,11 @@ class DriverDSLImpl(
|
|||||||
className = "net.corda.node.Corda", // cannot directly get class for this, so just use string
|
className = "net.corda.node.Corda", // cannot directly get class for this, so just use string
|
||||||
arguments = arguments,
|
arguments = arguments,
|
||||||
jdwpPort = debugPort,
|
jdwpPort = debugPort,
|
||||||
extraJvmArguments = extraJvmArguments + bytemanJvmArgs,
|
extraJvmArguments = extraJvmArguments + bytemanJvmArgs + "-Dnet.corda.node.printErrorsToStdErr=true",
|
||||||
workingDirectory = config.corda.baseDirectory,
|
workingDirectory = config.corda.baseDirectory,
|
||||||
maximumHeapSize = maximumHeapSize,
|
maximumHeapSize = maximumHeapSize,
|
||||||
classPath = cp
|
classPath = cp,
|
||||||
|
identifier = identifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,6 +167,25 @@ fun addressMustBeBoundFuture(executorService: ScheduledExecutorService, hostAndP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun nodeMustBeStartedFuture(
|
||||||
|
executorService: ScheduledExecutorService,
|
||||||
|
hostAndPort: NetworkHostAndPort,
|
||||||
|
listenProcess: Process? = null,
|
||||||
|
exception: () -> NodeListenProcessDeathException
|
||||||
|
): CordaFuture<Unit> {
|
||||||
|
return poll(executorService, "address $hostAndPort to bind") {
|
||||||
|
if (listenProcess != null && !listenProcess.isAlive) {
|
||||||
|
throw exception()
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Socket(hostAndPort.host, hostAndPort.port).close()
|
||||||
|
Unit
|
||||||
|
} catch (_exception: SocketException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The default timeout value of 40 seconds have been chosen based on previous node shutdown time estimate.
|
* The default timeout value of 40 seconds have been chosen based on previous node shutdown time estimate.
|
||||||
* It's been observed that nodes can take up to 30 seconds to shut down, so just to stay on the safe side the 60 seconds
|
* It's been observed that nodes can take up to 30 seconds to shut down, so just to stay on the safe side the 60 seconds
|
||||||
@ -221,6 +240,14 @@ fun <A> poll(
|
|||||||
class ListenProcessDeathException(hostAndPort: NetworkHostAndPort, listenProcess: Process) :
|
class ListenProcessDeathException(hostAndPort: NetworkHostAndPort, listenProcess: Process) :
|
||||||
CordaException("The process that was expected to listen on $hostAndPort has died with status: ${listenProcess.exitValue()}")
|
CordaException("The process that was expected to listen on $hostAndPort has died with status: ${listenProcess.exitValue()}")
|
||||||
|
|
||||||
|
class NodeListenProcessDeathException(hostAndPort: NetworkHostAndPort, listenProcess: Process, val causeFromStdError: String) :
|
||||||
|
CordaException(
|
||||||
|
"""
|
||||||
|
The node that was expected to start with $hostAndPort has died with status: ${listenProcess.exitValue()}
|
||||||
|
Error that caused the process to fail -> $causeFromStdError
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
|
||||||
fun <T> StartedNodeServices.startFlow(logic: FlowLogic<T>): FlowStateMachine<T> = startFlow(logic, newContext()).getOrThrow()
|
fun <T> StartedNodeServices.startFlow(logic: FlowLogic<T>): FlowStateMachine<T> = startFlow(logic, newContext()).getOrThrow()
|
||||||
|
|
||||||
fun StartedNodeServices.newContext(): InvocationContext = testContext(myInfo.chooseIdentity().name)
|
fun StartedNodeServices.newContext(): InvocationContext = testContext(myInfo.chooseIdentity().name)
|
||||||
|
@ -3,8 +3,6 @@ package net.corda.testing.node.internal
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.time.ZonedDateTime
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
object ProcessUtilities {
|
object ProcessUtilities {
|
||||||
inline fun <reified C : Any> startJavaProcess(
|
inline fun <reified C : Any> startJavaProcess(
|
||||||
@ -18,6 +16,7 @@ object ProcessUtilities {
|
|||||||
return startJavaProcess(C::class.java.name, arguments, classPath, workingDirectory, jdwpPort, extraJvmArguments, maximumHeapSize)
|
return startJavaProcess(C::class.java.name, arguments, classPath, workingDirectory, jdwpPort, extraJvmArguments, maximumHeapSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("LongParameterList")
|
||||||
fun startJavaProcess(
|
fun startJavaProcess(
|
||||||
className: String,
|
className: String,
|
||||||
arguments: List<String>,
|
arguments: List<String>,
|
||||||
@ -25,7 +24,8 @@ object ProcessUtilities {
|
|||||||
workingDirectory: Path? = null,
|
workingDirectory: Path? = null,
|
||||||
jdwpPort: Int? = null,
|
jdwpPort: Int? = null,
|
||||||
extraJvmArguments: List<String> = emptyList(),
|
extraJvmArguments: List<String> = emptyList(),
|
||||||
maximumHeapSize: String? = null
|
maximumHeapSize: String? = null,
|
||||||
|
identifier: String = ""
|
||||||
): Process {
|
): Process {
|
||||||
val command = mutableListOf<String>().apply {
|
val command = mutableListOf<String>().apply {
|
||||||
add(javaPath)
|
add(javaPath)
|
||||||
@ -40,11 +40,10 @@ object ProcessUtilities {
|
|||||||
inheritIO()
|
inheritIO()
|
||||||
environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator)
|
environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator)
|
||||||
if (workingDirectory != null) {
|
if (workingDirectory != null) {
|
||||||
// Timestamp may be handy if the same process started, killed and then re-started. Without timestamp
|
// An identifier may be handy if the same process started, killed and then re-started. Without the identifier
|
||||||
// StdOut and StdErr will be overwritten.
|
// StdOut and StdErr will be overwritten. By default the identifier is a timestamp passed down to here.
|
||||||
val timestamp = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss.SSS"))
|
redirectError((workingDirectory / "$className.$identifier.stderr.log").toFile())
|
||||||
redirectError((workingDirectory / "$className.$timestamp.stderr.log").toFile())
|
redirectOutput((workingDirectory / "$className.$identifier.stdout.log").toFile())
|
||||||
redirectOutput((workingDirectory / "$className.$timestamp.stdout.log").toFile())
|
|
||||||
directory(workingDirectory.toFile())
|
directory(workingDirectory.toFile())
|
||||||
}
|
}
|
||||||
}.start()
|
}.start()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user