mirror of
https://github.com/corda/corda.git
synced 2025-04-27 22:39:44 +00:00
[CORDA-1763]: Add node CLI option for validating configuration. (#4121)
This commit is contained in:
parent
5086358834
commit
6022cecca5
@ -7,7 +7,6 @@ import java.util.Collections.emptySet
|
|||||||
* It wraps either a valid [TARGET] or a set of [ERROR].
|
* It wraps either a valid [TARGET] or a set of [ERROR].
|
||||||
*/
|
*/
|
||||||
interface Validated<TARGET, ERROR> {
|
interface Validated<TARGET, ERROR> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The valid [TARGET] value.
|
* The valid [TARGET] value.
|
||||||
*
|
*
|
||||||
@ -30,6 +29,11 @@ interface Validated<TARGET, ERROR> {
|
|||||||
*/
|
*/
|
||||||
val isInvalid: Boolean get() = !isValid
|
val isInvalid: Boolean get() = !isValid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the underlying value as optional, with a null result instead of an exception if validation rules were violated.
|
||||||
|
*/
|
||||||
|
val optional: TARGET? get() = if (isValid) value else null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a valid [TARGET] if no validation errors are present. Otherwise, it throws the exception produced by [exceptionOnErrors], defaulting to [IllegalStateException].
|
* Returns a valid [TARGET] if no validation errors are present. Otherwise, it throws the exception produced by [exceptionOnErrors], defaulting to [IllegalStateException].
|
||||||
*
|
*
|
||||||
@ -52,8 +56,29 @@ interface Validated<TARGET, ERROR> {
|
|||||||
*/
|
*/
|
||||||
fun <MAPPED_ERROR> mapErrors(convertError: (ERROR) -> MAPPED_ERROR): Validated<TARGET, MAPPED_ERROR>
|
fun <MAPPED_ERROR> mapErrors(convertError: (ERROR) -> MAPPED_ERROR): Validated<TARGET, MAPPED_ERROR>
|
||||||
|
|
||||||
companion object {
|
/**
|
||||||
|
* Performs the given [action] if the underlying value is valid.
|
||||||
|
* @return itself for fluent chained invocation.
|
||||||
|
*/
|
||||||
|
fun doIfValid(action: (TARGET) -> Unit): Validated<TARGET, ERROR> {
|
||||||
|
if (isValid) {
|
||||||
|
action.invoke(value)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the given [action] if the underlying value is invalid.
|
||||||
|
* @return itself for fluent chained invocation.
|
||||||
|
*/
|
||||||
|
fun doOnErrors(action: (Set<ERROR>) -> Unit): Validated<TARGET, ERROR> {
|
||||||
|
if (isInvalid) {
|
||||||
|
action.invoke(errors)
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Constructs a [Validated] wrapper with given valid [target] value and no errors.
|
* Constructs a [Validated] wrapper with given valid [target] value and no errors.
|
||||||
*/
|
*/
|
||||||
@ -82,28 +107,23 @@ interface Validated<TARGET, ERROR> {
|
|||||||
* Models the result of validating a [TARGET] value, producing [ERROR]s if rules are violated.
|
* Models the result of validating a [TARGET] value, producing [ERROR]s if rules are violated.
|
||||||
*/
|
*/
|
||||||
sealed class Result<TARGET, ERROR> : Validated<TARGET, ERROR> {
|
sealed class Result<TARGET, ERROR> : Validated<TARGET, ERROR> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A successful validation result, containing a valid [TARGET] value and no [ERROR]s.
|
* A successful validation result, containing a valid [TARGET] value and no [ERROR]s.
|
||||||
*/
|
*/
|
||||||
class Successful<TARGET, ERROR>(override val value: TARGET) : Result<TARGET, ERROR>(), Validated<TARGET, ERROR> {
|
class Successful<TARGET, ERROR>(override val value: TARGET) : Result<TARGET, ERROR>(), Validated<TARGET, ERROR> {
|
||||||
|
|
||||||
override val errors: Set<ERROR> = emptySet<ERROR>()
|
override val errors: Set<ERROR> = emptySet<ERROR>()
|
||||||
|
|
||||||
override fun valueOrThrow(exceptionOnErrors: (Set<ERROR>) -> Exception) = value
|
override fun valueOrThrow(exceptionOnErrors: (Set<ERROR>) -> Exception) = value
|
||||||
|
|
||||||
override fun <MAPPED> map(convert: (TARGET) -> MAPPED): Validated<MAPPED, ERROR> {
|
override fun <MAPPED> map(convert: (TARGET) -> MAPPED): Validated<MAPPED, ERROR> {
|
||||||
|
|
||||||
return valid(convert.invoke(value))
|
return valid(convert.invoke(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <MAPPED> mapValid(convert: (TARGET) -> Validated<MAPPED, ERROR>): Validated<MAPPED, ERROR> {
|
override fun <MAPPED> mapValid(convert: (TARGET) -> Validated<MAPPED, ERROR>): Validated<MAPPED, ERROR> {
|
||||||
|
|
||||||
return convert.invoke(value)
|
return convert.invoke(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <MAPPED_ERROR> mapErrors(convertError: (ERROR) -> MAPPED_ERROR): Validated<TARGET, MAPPED_ERROR> {
|
override fun <MAPPED_ERROR> mapErrors(convertError: (ERROR) -> MAPPED_ERROR): Validated<TARGET, MAPPED_ERROR> {
|
||||||
|
|
||||||
return valid(value)
|
return valid(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +132,6 @@ interface Validated<TARGET, ERROR> {
|
|||||||
* An unsuccessful validation result, containing [ERROR]s and no valid [TARGET] value.
|
* An unsuccessful validation result, containing [ERROR]s and no valid [TARGET] value.
|
||||||
*/
|
*/
|
||||||
class Unsuccessful<TARGET, ERROR>(override val errors: Set<ERROR>) : Result<TARGET, ERROR>(), Validated<TARGET, ERROR> {
|
class Unsuccessful<TARGET, ERROR>(override val errors: Set<ERROR>) : Result<TARGET, ERROR>(), Validated<TARGET, ERROR> {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(errors.isNotEmpty())
|
require(errors.isNotEmpty())
|
||||||
}
|
}
|
||||||
@ -122,17 +141,14 @@ interface Validated<TARGET, ERROR> {
|
|||||||
override fun valueOrThrow(exceptionOnErrors: (Set<ERROR>) -> Exception) = throw exceptionOnErrors.invoke(errors)
|
override fun valueOrThrow(exceptionOnErrors: (Set<ERROR>) -> Exception) = throw exceptionOnErrors.invoke(errors)
|
||||||
|
|
||||||
override fun <MAPPED> map(convert: (TARGET) -> MAPPED): Validated<MAPPED, ERROR> {
|
override fun <MAPPED> map(convert: (TARGET) -> MAPPED): Validated<MAPPED, ERROR> {
|
||||||
|
|
||||||
return invalid(errors)
|
return invalid(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <MAPPED> mapValid(convert: (TARGET) -> Validated<MAPPED, ERROR>): Validated<MAPPED, ERROR> {
|
override fun <MAPPED> mapValid(convert: (TARGET) -> Validated<MAPPED, ERROR>): Validated<MAPPED, ERROR> {
|
||||||
|
|
||||||
return invalid(errors)
|
return invalid(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <MAPPED_ERROR> mapErrors(convertError: (ERROR) -> MAPPED_ERROR): Validated<TARGET, MAPPED_ERROR> {
|
override fun <MAPPED_ERROR> mapErrors(convertError: (ERROR) -> MAPPED_ERROR): Validated<TARGET, MAPPED_ERROR> {
|
||||||
|
|
||||||
return invalid(errors.asSequence().map(convertError).toSet())
|
return invalid(errors.asSequence().map(convertError).toSet())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Configuration status="info">
|
<Configuration status="debug">
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<Property name="log-path">${sys:log-path:-logs}</Property>
|
<Property name="log-path">${sys:log-path:-logs}</Property>
|
||||||
<Property name="log-name">node-${hostName}</Property>
|
<Property name="log-name">node-${hostName}</Property>
|
||||||
<Property name="archive">${log-path}/archive</Property>
|
<Property name="archive">${log-path}/archive</Property>
|
||||||
<Property name="consoleLogLevel">${sys:consoleLogLevel:-error}</Property>
|
<Property name="defaultLogLevel">${sys:log4j2.level:-info}</Property>
|
||||||
<Property name="defaultLogLevel">${sys:defaultLogLevel:-info}</Property>
|
<Property name="consoleLogLevel">${sys:consoleLogLevel:-$defaultLogLevel}</Property>
|
||||||
|
<Property name="fileLogLevel">${sys:fileLogLevel:-$defaultLogLevel}</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
|
|
||||||
<ThresholdFilter level="trace"/>
|
|
||||||
|
|
||||||
<Appenders>
|
<Appenders>
|
||||||
<Console name="Console-Appender" target="SYSTEM_OUT">
|
<Console name="Console-Appender" target="SYSTEM_OUT">
|
||||||
<PatternLayout>
|
<PatternLayout>
|
||||||
<ScriptPatternSelector defaultPattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n %throwable{0}}{INFO=white,WARN=red,FATAL=bright red}">
|
<ScriptPatternSelector defaultPattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg%n%throwable{short.message}}{INFO=white,WARN=red,FATAL=bright red}">
|
||||||
<Script name="MDCSelector" language="javascript"><![CDATA[
|
<Script name="MDCSelector" language="javascript"><![CDATA[
|
||||||
result = null;
|
result = null;
|
||||||
if (!logEvent.getContextData().size() == 0) {
|
if (!logEvent.getContextData().size() == 0) {
|
||||||
@ -25,15 +24,14 @@
|
|||||||
result;
|
result;
|
||||||
]]>
|
]]>
|
||||||
</Script>
|
</Script>
|
||||||
<PatternMatch key="WithMDC" pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg %X%n}{INFO=white,WARN=red,FATAL=bright red}"/>
|
<PatternMatch key="WithMDC" pattern="%highlight{[%level{length=5}] %date{HH:mm:ssZ} [%t] %c{2}.%method - %msg %X%n%throwable{short.message}}{INFO=white,WARN=red,FATAL=bright red}"/>
|
||||||
</ScriptPatternSelector>
|
</ScriptPatternSelector>
|
||||||
</PatternLayout>
|
</PatternLayout>
|
||||||
<ThresholdFilter level="trace"/>
|
|
||||||
</Console>
|
</Console>
|
||||||
|
|
||||||
<!-- Required for printBasicInfo -->
|
<!-- Required for printBasicInfo -->
|
||||||
<Console name="Console-Appender-Println" target="SYSTEM_OUT">
|
<Console name="Console-Appender-Println" target="SYSTEM_OUT">
|
||||||
<PatternLayout pattern="%msg%n%throwable{0}" />
|
<PatternLayout pattern="%msg%n%throwable{short.message}" />
|
||||||
</Console>
|
</Console>
|
||||||
|
|
||||||
<!-- Will generate up to 100 log files for a given day. During every rollover it will delete
|
<!-- Will generate up to 100 log files for a given day. During every rollover it will delete
|
||||||
@ -42,7 +40,21 @@
|
|||||||
fileName="${log-path}/${log-name}.log"
|
fileName="${log-path}/${log-name}.log"
|
||||||
filePattern="${archive}/${log-name}.%date{yyyy-MM-dd}-%i.log.gz">
|
filePattern="${archive}/${log-name}.%date{yyyy-MM-dd}-%i.log.gz">
|
||||||
|
|
||||||
<PatternLayout pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg %X%n"/>
|
<PatternLayout>
|
||||||
|
<ScriptPatternSelector defaultPattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg%n">
|
||||||
|
<Script name="MDCSelector" language="javascript"><![CDATA[
|
||||||
|
result = null;
|
||||||
|
if (!logEvent.getContextData().size() == 0) {
|
||||||
|
result = "WithMDC";
|
||||||
|
} else {
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
result;
|
||||||
|
]]>
|
||||||
|
</Script>
|
||||||
|
<PatternMatch key="WithMDC" pattern="[%-5level] %date{ISO8601}{UTC}Z [%t] %c{2}.%method - %msg %X%n"/>
|
||||||
|
</ScriptPatternSelector>
|
||||||
|
</PatternLayout>
|
||||||
|
|
||||||
<Policies>
|
<Policies>
|
||||||
<TimeBasedTriggeringPolicy/>
|
<TimeBasedTriggeringPolicy/>
|
||||||
@ -66,7 +78,7 @@
|
|||||||
<Loggers>
|
<Loggers>
|
||||||
<Root level="${defaultLogLevel}">
|
<Root level="${defaultLogLevel}">
|
||||||
<AppenderRef ref="Console-Appender" level="${consoleLogLevel}"/>
|
<AppenderRef ref="Console-Appender" level="${consoleLogLevel}"/>
|
||||||
<AppenderRef ref="RollingFile-Appender" />
|
<AppenderRef ref="RollingFile-Appender" level="${fileLogLevel}"/>
|
||||||
</Root>
|
</Root>
|
||||||
<Logger name="BasicInfo" additivity="false">
|
<Logger name="BasicInfo" additivity="false">
|
||||||
<AppenderRef ref="Console-Appender-Println"/>
|
<AppenderRef ref="Console-Appender-Println"/>
|
||||||
|
@ -7,6 +7,8 @@ release, see :doc:`upgrade-notes`.
|
|||||||
Unreleased
|
Unreleased
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
* New "validate-configuration" sub-command to `corda.jar`, allowing to validate the actual node configuration without starting the node.
|
||||||
|
|
||||||
* Introduced new optional network bootstrapper command line option (--minimum-platform-version) to set as a network parameter
|
* Introduced new optional network bootstrapper command line option (--minimum-platform-version) to set as a network parameter
|
||||||
|
|
||||||
* Introduce minimum and target platform version for CorDapps.
|
* Introduce minimum and target platform version for CorDapps.
|
||||||
|
@ -77,6 +77,9 @@ Parameters:
|
|||||||
|
|
||||||
``install-shell-extensions``: Install ``corda`` alias and auto completion for bash and zsh. See :doc:`cli-application-shell-extensions` for more info.
|
``install-shell-extensions``: Install ``corda`` alias and auto completion for bash and zsh. See :doc:`cli-application-shell-extensions` for more info.
|
||||||
|
|
||||||
|
|
||||||
|
``validate-configuration``: Validates the actual configuration without starting the node.
|
||||||
|
|
||||||
.. _enabling-remote-debugging:
|
.. _enabling-remote-debugging:
|
||||||
|
|
||||||
Enabling remote debugging
|
Enabling remote debugging
|
||||||
|
@ -72,6 +72,7 @@ dependencies {
|
|||||||
compile project(':client:rpc')
|
compile project(':client:rpc')
|
||||||
compile project(':tools:shell')
|
compile project(':tools:shell')
|
||||||
compile project(':tools:cliutils')
|
compile project(':tools:cliutils')
|
||||||
|
compile project(':common-validation')
|
||||||
|
|
||||||
// Log4J: logging framework (with SLF4J bindings)
|
// Log4J: logging framework (with SLF4J bindings)
|
||||||
compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
|
compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
|
||||||
|
@ -2,11 +2,9 @@ package net.corda.node
|
|||||||
|
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
import com.typesafe.config.ConfigRenderOptions
|
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.node.services.config.ConfigHelper
|
import net.corda.node.services.config.ConfigHelper
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.config.NodeConfigurationImpl
|
|
||||||
import net.corda.node.services.config.parseAsNodeConfiguration
|
import net.corda.node.services.config.parseAsNodeConfiguration
|
||||||
import net.corda.nodeapi.internal.config.UnknownConfigKeysPolicy
|
import net.corda.nodeapi.internal.config.UnknownConfigKeysPolicy
|
||||||
import picocli.CommandLine.Option
|
import picocli.CommandLine.Option
|
||||||
@ -39,20 +37,9 @@ open class SharedNodeCmdLineOptions {
|
|||||||
)
|
)
|
||||||
var devMode: Boolean? = null
|
var devMode: Boolean? = null
|
||||||
|
|
||||||
open fun loadConfig(): NodeConfiguration {
|
open fun parseConfiguration(configuration: Config): NodeConfiguration = configuration.parseAsNodeConfiguration(unknownConfigKeysPolicy::handle)
|
||||||
return getRawConfig().parseAsNodeConfiguration(unknownConfigKeysPolicy::handle)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun getRawConfig(): Config {
|
open fun rawConfiguration(): Config = ConfigHelper.loadConfig(baseDirectory, configFile)
|
||||||
val rawConfig = ConfigHelper.loadConfig(
|
|
||||||
baseDirectory,
|
|
||||||
configFile
|
|
||||||
)
|
|
||||||
if (devMode == true) {
|
|
||||||
println("Config:\n${rawConfig.root().render(ConfigRenderOptions.defaults())}")
|
|
||||||
}
|
|
||||||
return rawConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
fun copyFrom(other: SharedNodeCmdLineOptions) {
|
fun copyFrom(other: SharedNodeCmdLineOptions) {
|
||||||
baseDirectory = other.baseDirectory
|
baseDirectory = other.baseDirectory
|
||||||
@ -63,8 +50,8 @@ open class SharedNodeCmdLineOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class InitialRegistrationCmdLineOptions : SharedNodeCmdLineOptions() {
|
class InitialRegistrationCmdLineOptions : SharedNodeCmdLineOptions() {
|
||||||
override fun loadConfig(): NodeConfiguration {
|
override fun parseConfiguration(configuration: Config): NodeConfiguration {
|
||||||
return getRawConfig().parseAsNodeConfiguration(unknownConfigKeysPolicy::handle).also { config ->
|
return super.parseConfiguration(configuration).also { config ->
|
||||||
require(!config.devMode) { "Registration cannot occur in development mode" }
|
require(!config.devMode) { "Registration cannot occur in development mode" }
|
||||||
require(config.compatibilityZoneURL != null || config.networkServices != null) {
|
require(config.compatibilityZoneURL != null || config.networkServices != null) {
|
||||||
"compatibilityZoneURL or networkServices must be present in the node configuration file in registration mode."
|
"compatibilityZoneURL or networkServices must be present in the node configuration file in registration mode."
|
||||||
@ -134,15 +121,8 @@ open class NodeCmdLineOptions : SharedNodeCmdLineOptions() {
|
|||||||
)
|
)
|
||||||
var networkRootTrustStorePassword: String? = null
|
var networkRootTrustStorePassword: String? = null
|
||||||
|
|
||||||
override fun loadConfig(): NodeConfiguration {
|
override fun parseConfiguration(configuration: Config): NodeConfiguration {
|
||||||
val rawConfig = ConfigHelper.loadConfig(
|
return super.parseConfiguration(configuration).also { config ->
|
||||||
baseDirectory,
|
|
||||||
configFile,
|
|
||||||
configOverrides = ConfigFactory.parseMap(mapOf("noLocalShell" to this.noLocalShell) +
|
|
||||||
if (sshdServer) mapOf("sshd" to mapOf("port" to sshdServerPort.toString())) else emptyMap<String, Any>() +
|
|
||||||
if (devMode != null) mapOf("devMode" to this.devMode) else emptyMap())
|
|
||||||
)
|
|
||||||
return rawConfig.parseAsNodeConfiguration(unknownConfigKeysPolicy::handle).also { config ->
|
|
||||||
if (isRegistration) {
|
if (isRegistration) {
|
||||||
require(!config.devMode) { "Registration cannot occur in development mode" }
|
require(!config.devMode) { "Registration cannot occur in development mode" }
|
||||||
require(config.compatibilityZoneURL != null || config.networkServices != null) {
|
require(config.compatibilityZoneURL != null || config.networkServices != null) {
|
||||||
@ -151,6 +131,18 @@ open class NodeCmdLineOptions : SharedNodeCmdLineOptions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun rawConfiguration(): Config {
|
||||||
|
val configOverrides = mutableMapOf<String, Any>()
|
||||||
|
configOverrides += "noLocalShell" to noLocalShell
|
||||||
|
if (sshdServer) {
|
||||||
|
configOverrides += "sshd" to mapOf("port" to sshdServerPort.toString())
|
||||||
|
}
|
||||||
|
devMode?.let {
|
||||||
|
configOverrides += "devMode" to it
|
||||||
|
}
|
||||||
|
return ConfigHelper.loadConfig(baseDirectory, configFile, configOverrides = ConfigFactory.parseMap(configOverrides))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package net.corda.node.internal
|
package net.corda.node.internal
|
||||||
|
|
||||||
import com.typesafe.config.ConfigException
|
|
||||||
import io.netty.channel.unix.Errors
|
import io.netty.channel.unix.Errors
|
||||||
import net.corda.cliutils.*
|
import net.corda.cliutils.*
|
||||||
import net.corda.core.crypto.Crypto
|
import net.corda.core.crypto.Crypto
|
||||||
@ -15,12 +14,12 @@ import net.corda.node.*
|
|||||||
import net.corda.node.internal.Node.Companion.isInvalidJavaVersion
|
import net.corda.node.internal.Node.Companion.isInvalidJavaVersion
|
||||||
import net.corda.node.internal.cordapp.MultipleCordappsForFlowException
|
import net.corda.node.internal.cordapp.MultipleCordappsForFlowException
|
||||||
import net.corda.node.internal.subcommands.*
|
import net.corda.node.internal.subcommands.*
|
||||||
|
import net.corda.node.internal.subcommands.ValidateConfigurationCli.Companion.logConfigurationErrors
|
||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.services.config.shouldStartLocalShell
|
import net.corda.node.services.config.shouldStartLocalShell
|
||||||
import net.corda.node.services.config.shouldStartSSHDaemon
|
import net.corda.node.services.config.shouldStartSSHDaemon
|
||||||
import net.corda.node.utilities.registration.NodeRegistrationException
|
import net.corda.node.utilities.registration.NodeRegistrationException
|
||||||
import net.corda.nodeapi.internal.addShutdownHook
|
import net.corda.nodeapi.internal.addShutdownHook
|
||||||
import net.corda.nodeapi.internal.config.UnknownConfigurationKeysException
|
|
||||||
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
|
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
|
||||||
import net.corda.nodeapi.internal.persistence.DatabaseIncompatibleException
|
import net.corda.nodeapi.internal.persistence.DatabaseIncompatibleException
|
||||||
import net.corda.tools.shell.InteractiveShell
|
import net.corda.tools.shell.InteractiveShell
|
||||||
@ -57,15 +56,18 @@ abstract class NodeCliCommand(alias: String, description: String, val startup: N
|
|||||||
/** Main corda entry point. */
|
/** Main corda entry point. */
|
||||||
open class NodeStartupCli : CordaCliWrapper("corda", "Runs a Corda Node") {
|
open class NodeStartupCli : CordaCliWrapper("corda", "Runs a Corda Node") {
|
||||||
open val startup = NodeStartup()
|
open val startup = NodeStartup()
|
||||||
|
@Mixin
|
||||||
|
val cmdLineOptions = NodeCmdLineOptions()
|
||||||
|
|
||||||
private val networkCacheCli by lazy { ClearNetworkCacheCli(startup) }
|
private val networkCacheCli by lazy { ClearNetworkCacheCli(startup) }
|
||||||
private val justGenerateNodeInfoCli by lazy { GenerateNodeInfoCli(startup) }
|
private val justGenerateNodeInfoCli by lazy { GenerateNodeInfoCli(startup) }
|
||||||
private val justGenerateRpcSslCertsCli by lazy { GenerateRpcSslCertsCli(startup) }
|
private val justGenerateRpcSslCertsCli by lazy { GenerateRpcSslCertsCli(startup) }
|
||||||
private val initialRegistrationCli by lazy { InitialRegistrationCli(startup) }
|
private val initialRegistrationCli by lazy { InitialRegistrationCli(startup) }
|
||||||
|
private val validateConfigurationCli by lazy { ValidateConfigurationCli() }
|
||||||
|
|
||||||
override fun initLogging() = this.initLogging(cmdLineOptions.baseDirectory)
|
override fun initLogging() = this.initLogging(cmdLineOptions.baseDirectory)
|
||||||
|
|
||||||
override fun additionalSubCommands() = setOf(networkCacheCli, justGenerateNodeInfoCli, justGenerateRpcSslCertsCli, initialRegistrationCli)
|
override fun additionalSubCommands() = setOf(networkCacheCli, justGenerateNodeInfoCli, justGenerateRpcSslCertsCli, initialRegistrationCli, validateConfigurationCli)
|
||||||
|
|
||||||
override fun runProgram(): Int {
|
override fun runProgram(): Int {
|
||||||
return when {
|
return when {
|
||||||
@ -104,9 +106,6 @@ open class NodeStartupCli : CordaCliWrapper("corda", "Runs a Corda Node") {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mixin
|
|
||||||
val cmdLineOptions = NodeCmdLineOptions()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This class provides a common set of functionality for starting a Node from command line arguments. */
|
/** This class provides a common set of functionality for starting a Node from command line arguments. */
|
||||||
@ -140,13 +139,7 @@ open class NodeStartup : NodeStartupLogging {
|
|||||||
Node.printBasicNodeInfo(LOGS_CAN_BE_FOUND_IN_STRING, System.getProperty("log-path"))
|
Node.printBasicNodeInfo(LOGS_CAN_BE_FOUND_IN_STRING, System.getProperty("log-path"))
|
||||||
|
|
||||||
// Step 5. Load and validate node configuration.
|
// Step 5. Load and validate node configuration.
|
||||||
val configuration = (attempt { cmdLineOptions.loadConfig() }.doOnException(handleConfigurationLoadingError(cmdLineOptions.configFile)) as? Try.Success)?.let(Try.Success<NodeConfiguration>::value)
|
val configuration = cmdLineOptions.nodeConfiguration().doOnErrors { errors -> logConfigurationErrors(errors, cmdLineOptions.configFile) }.optional ?: return ExitCodes.FAILURE
|
||||||
?: return ExitCodes.FAILURE
|
|
||||||
val errors = configuration.validate()
|
|
||||||
if (errors.isNotEmpty()) {
|
|
||||||
logger.error("Invalid node configuration. Errors were:${System.lineSeparator()}${errors.joinToString(System.lineSeparator())}")
|
|
||||||
return ExitCodes.FAILURE
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 6. Configuring special serialisation requirements, i.e., bft-smart relies on Java serialization.
|
// Step 6. Configuring special serialisation requirements, i.e., bft-smart relies on Java serialization.
|
||||||
attempt { banJavaSerialisation(configuration) }.doOnException { error -> error.logAsUnexpected("Exception while configuring serialisation") } as? Try.Success
|
attempt { banJavaSerialisation(configuration) }.doOnException { error -> error.logAsUnexpected("Exception while configuring serialisation") } as? Try.Success
|
||||||
@ -430,23 +423,6 @@ interface NodeStartupLogging {
|
|||||||
else -> error.logAsUnexpected("Exception during node startup")
|
else -> error.logAsUnexpected("Exception during node startup")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleConfigurationLoadingError(configFile: Path) = { error: Exception ->
|
|
||||||
when (error) {
|
|
||||||
is UnknownConfigurationKeysException -> error.logAsExpected()
|
|
||||||
is ConfigException.IO -> error.logAsExpected(configFileNotFoundMessage(configFile), ::println)
|
|
||||||
else -> error.logAsUnexpected("Unexpected error whilst reading node configuration")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun configFileNotFoundMessage(configFile: Path): String {
|
|
||||||
return """
|
|
||||||
Unable to load the node config file from '$configFile'.
|
|
||||||
|
|
||||||
Try setting the --base-directory flag to change which directory the node
|
|
||||||
is looking in, or use the --config-file flag to specify it explicitly.
|
|
||||||
""".trimIndent()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun CliWrapperBase.initLogging(baseDirectory: Path) {
|
fun CliWrapperBase.initLogging(baseDirectory: Path) {
|
||||||
|
@ -5,7 +5,7 @@ import net.corda.node.internal.NodeCliCommand
|
|||||||
import net.corda.node.internal.NodeStartup
|
import net.corda.node.internal.NodeStartup
|
||||||
import net.corda.node.internal.RunAfterNodeInitialisation
|
import net.corda.node.internal.RunAfterNodeInitialisation
|
||||||
|
|
||||||
class ClearNetworkCacheCli(startup: NodeStartup): NodeCliCommand("clear-network-cache", "Clears local copy of network map, on node startup it will be restored from server or file system.", startup) {
|
class ClearNetworkCacheCli(startup: NodeStartup): NodeCliCommand("clear-network-cache", "Clear local copy of network map, on node startup it will be restored from server or file system.", startup) {
|
||||||
override fun runProgram(): Int {
|
override fun runProgram(): Int {
|
||||||
return startup.initialiseAndRun(cmdLineOptions, object: RunAfterNodeInitialisation {
|
return startup.initialiseAndRun(cmdLineOptions, object: RunAfterNodeInitialisation {
|
||||||
override fun run(node: Node) = node.clearNetworkMapCache()
|
override fun run(node: Node) = node.clearNetworkMapCache()
|
||||||
|
@ -5,7 +5,7 @@ import net.corda.node.internal.NodeCliCommand
|
|||||||
import net.corda.node.internal.NodeStartup
|
import net.corda.node.internal.NodeStartup
|
||||||
import net.corda.node.internal.RunAfterNodeInitialisation
|
import net.corda.node.internal.RunAfterNodeInitialisation
|
||||||
|
|
||||||
class GenerateNodeInfoCli(startup: NodeStartup): NodeCliCommand("generate-node-info", "Performs the node start-up tasks necessary to generate the nodeInfo file, saves it to disk, then exits.", startup) {
|
class GenerateNodeInfoCli(startup: NodeStartup): NodeCliCommand("generate-node-info", "Perform the node start-up tasks necessary to generate the nodeInfo file, save it to disk, then exit.", startup) {
|
||||||
override fun runProgram(): Int {
|
override fun runProgram(): Int {
|
||||||
return startup.initialiseAndRun(cmdLineOptions, object : RunAfterNodeInitialisation {
|
return startup.initialiseAndRun(cmdLineOptions, object : RunAfterNodeInitialisation {
|
||||||
override fun run(node: Node) {
|
override fun run(node: Node) {
|
||||||
|
@ -13,7 +13,7 @@ import net.corda.node.utilities.saveToTrustStore
|
|||||||
import java.io.Console
|
import java.io.Console
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
class GenerateRpcSslCertsCli(startup: NodeStartup): NodeCliCommand("generate-rpc-ssl-settings", "Generates the SSL key and trust stores for a secure RPC connection.", startup) {
|
class GenerateRpcSslCertsCli(startup: NodeStartup): NodeCliCommand("generate-rpc-ssl-settings", "Generate the SSL key and trust stores for a secure RPC connection.", startup) {
|
||||||
override fun runProgram(): Int {
|
override fun runProgram(): Int {
|
||||||
return startup.initialiseAndRun(cmdLineOptions, GenerateRpcSslCerts())
|
return startup.initialiseAndRun(cmdLineOptions, GenerateRpcSslCerts())
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import picocli.CommandLine.Option
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
class InitialRegistrationCli(val startup: NodeStartup): CliWrapperBase("initial-registration", "Starts initial node registration with Corda network to obtain certificate from the permissioning server.") {
|
class InitialRegistrationCli(val startup: NodeStartup): CliWrapperBase("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.") {
|
||||||
@Option(names = ["-t", "--network-root-truststore"], description = ["Network root trust store obtained from network operator."])
|
@Option(names = ["-t", "--network-root-truststore"], description = ["Network root trust store obtained from network operator."])
|
||||||
var networkRootTrustStorePathParameter: Path? = null
|
var networkRootTrustStorePathParameter: Path? = null
|
||||||
|
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
package net.corda.node.internal.subcommands
|
||||||
|
|
||||||
|
import com.typesafe.config.Config
|
||||||
|
import com.typesafe.config.ConfigException
|
||||||
|
import com.typesafe.config.ConfigRenderOptions
|
||||||
|
import net.corda.cliutils.CliWrapperBase
|
||||||
|
import net.corda.cliutils.ExitCodes
|
||||||
|
import net.corda.common.validation.internal.Validated
|
||||||
|
import net.corda.common.validation.internal.Validated.Companion.invalid
|
||||||
|
import net.corda.common.validation.internal.Validated.Companion.valid
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
|
import net.corda.node.SharedNodeCmdLineOptions
|
||||||
|
import net.corda.node.internal.initLogging
|
||||||
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
|
import picocli.CommandLine.*
|
||||||
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
internal class ValidateConfigurationCli : CliWrapperBase("validate-configuration", "Validate the configuration without starting the node.") {
|
||||||
|
internal companion object {
|
||||||
|
private val logger = loggerFor<ValidateConfigurationCli>()
|
||||||
|
|
||||||
|
internal fun logConfigurationErrors(errors: Iterable<Exception>, configFile: Path) {
|
||||||
|
errors.forEach { error ->
|
||||||
|
when (error) {
|
||||||
|
is ConfigException.IO -> logger.error(configFileNotFoundMessage(configFile))
|
||||||
|
else -> logger.error("Error while parsing node configuration.", error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun configFileNotFoundMessage(configFile: Path): String {
|
||||||
|
return """
|
||||||
|
Unable to load the node config file from '$configFile'.
|
||||||
|
|
||||||
|
Try setting the --base-directory flag to change which directory the node
|
||||||
|
is looking in, or use the --config-file flag to specify it explicitly.
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Mixin
|
||||||
|
private val cmdLineOptions = SharedNodeCmdLineOptions()
|
||||||
|
|
||||||
|
override fun initLogging() = initLogging(cmdLineOptions.baseDirectory)
|
||||||
|
|
||||||
|
override fun runProgram(): Int {
|
||||||
|
val configuration = cmdLineOptions.nodeConfiguration()
|
||||||
|
if (configuration.isInvalid) {
|
||||||
|
logConfigurationErrors(configuration.errors, cmdLineOptions.configFile)
|
||||||
|
return ExitCodes.FAILURE
|
||||||
|
}
|
||||||
|
return ExitCodes.SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun SharedNodeCmdLineOptions.nodeConfiguration(): Valid<NodeConfiguration> = NodeConfigurationParser.invoke(this)
|
||||||
|
|
||||||
|
private object NodeConfigurationParser : (SharedNodeCmdLineOptions) -> Valid<NodeConfiguration> {
|
||||||
|
private val logger = loggerFor<ValidateConfigurationCli>()
|
||||||
|
|
||||||
|
private val configRenderingOptions = ConfigRenderOptions.defaults().setComments(false).setOriginComments(false).setFormatted(true)
|
||||||
|
|
||||||
|
override fun invoke(cmds: SharedNodeCmdLineOptions): Valid<NodeConfiguration> {
|
||||||
|
return attempt(cmds::rawConfiguration).doIfValid(::log).attemptMap(cmds::parseConfiguration).mapValid(::validate)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun log(config: Config) = logger.debug("Actual configuration:\n${config.root().render(configRenderingOptions)}")
|
||||||
|
|
||||||
|
private fun validate(configuration: NodeConfiguration): Valid<NodeConfiguration> {
|
||||||
|
return Validated.withResult(configuration, configuration.validate().asSequence().map { error -> IllegalArgumentException(error) }.toSet())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <VALUE, MAPPED> Valid<VALUE>.attemptMap(convert: (VALUE) -> MAPPED): Valid<MAPPED> = mapValid { value -> attempt { convert.invoke(value) } }
|
||||||
|
|
||||||
|
private fun <VALUE> attempt(action: () -> VALUE): Valid<VALUE> {
|
||||||
|
return try {
|
||||||
|
valid(action.invoke())
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
return invalid(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private typealias Valid<TARGET> = Validated<TARGET, Exception>
|
@ -777,6 +777,7 @@ class DriverDSLImpl(
|
|||||||
"visualvm.display.name" to "corda-${config.corda.myLegalName}"
|
"visualvm.display.name" to "corda-${config.corda.myLegalName}"
|
||||||
)
|
)
|
||||||
debugPort?.let {
|
debugPort?.let {
|
||||||
|
systemProperties += "log4j2.level" to "debug"
|
||||||
systemProperties += "log4j2.debug" to "true"
|
systemProperties += "log4j2.debug" to "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user