mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +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].
|
||||
*/
|
||||
interface Validated<TARGET, ERROR> {
|
||||
|
||||
/**
|
||||
* The valid [TARGET] value.
|
||||
*
|
||||
@ -30,6 +29,11 @@ interface Validated<TARGET, ERROR> {
|
||||
*/
|
||||
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].
|
||||
*
|
||||
@ -52,8 +56,29 @@ interface Validated<TARGET, 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.
|
||||
*/
|
||||
@ -82,28 +107,23 @@ interface Validated<TARGET, ERROR> {
|
||||
* Models the result of validating a [TARGET] value, producing [ERROR]s if rules are violated.
|
||||
*/
|
||||
sealed class Result<TARGET, ERROR> : Validated<TARGET, ERROR> {
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
|
||||
override val errors: Set<ERROR> = emptySet<ERROR>()
|
||||
|
||||
override fun valueOrThrow(exceptionOnErrors: (Set<ERROR>) -> Exception) = value
|
||||
|
||||
override fun <MAPPED> map(convert: (TARGET) -> MAPPED): Validated<MAPPED, ERROR> {
|
||||
|
||||
return valid(convert.invoke(value))
|
||||
}
|
||||
|
||||
override fun <MAPPED> mapValid(convert: (TARGET) -> Validated<MAPPED, ERROR>): Validated<MAPPED, ERROR> {
|
||||
|
||||
return convert.invoke(value)
|
||||
}
|
||||
|
||||
override fun <MAPPED_ERROR> mapErrors(convertError: (ERROR) -> MAPPED_ERROR): Validated<TARGET, MAPPED_ERROR> {
|
||||
|
||||
return valid(value)
|
||||
}
|
||||
}
|
||||
@ -112,7 +132,6 @@ interface Validated<TARGET, ERROR> {
|
||||
* 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> {
|
||||
|
||||
init {
|
||||
require(errors.isNotEmpty())
|
||||
}
|
||||
@ -122,17 +141,14 @@ interface Validated<TARGET, ERROR> {
|
||||
override fun valueOrThrow(exceptionOnErrors: (Set<ERROR>) -> Exception) = throw exceptionOnErrors.invoke(errors)
|
||||
|
||||
override fun <MAPPED> map(convert: (TARGET) -> MAPPED): Validated<MAPPED, ERROR> {
|
||||
|
||||
return invalid(errors)
|
||||
}
|
||||
|
||||
override fun <MAPPED> mapValid(convert: (TARGET) -> Validated<MAPPED, ERROR>): Validated<MAPPED, ERROR> {
|
||||
|
||||
return invalid(errors)
|
||||
}
|
||||
|
||||
override fun <MAPPED_ERROR> mapErrors(convertError: (ERROR) -> MAPPED_ERROR): Validated<TARGET, MAPPED_ERROR> {
|
||||
|
||||
return invalid(errors.asSequence().map(convertError).toSet())
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="info">
|
||||
<Configuration status="debug">
|
||||
|
||||
<Properties>
|
||||
<Property name="log-path">${sys:log-path:-logs}</Property>
|
||||
<Property name="log-name">node-${hostName}</Property>
|
||||
<Property name="archive">${log-path}/archive</Property>
|
||||
<Property name="consoleLogLevel">${sys:consoleLogLevel:-error}</Property>
|
||||
<Property name="defaultLogLevel">${sys:defaultLogLevel:-info}</Property>
|
||||
<Property name="defaultLogLevel">${sys:log4j2.level:-info}</Property>
|
||||
<Property name="consoleLogLevel">${sys:consoleLogLevel:-$defaultLogLevel}</Property>
|
||||
<Property name="fileLogLevel">${sys:fileLogLevel:-$defaultLogLevel}</Property>
|
||||
</Properties>
|
||||
|
||||
<ThresholdFilter level="trace"/>
|
||||
|
||||
<Appenders>
|
||||
<Console name="Console-Appender" target="SYSTEM_OUT">
|
||||
<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[
|
||||
result = null;
|
||||
if (!logEvent.getContextData().size() == 0) {
|
||||
@ -25,15 +24,14 @@
|
||||
result;
|
||||
]]>
|
||||
</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>
|
||||
</PatternLayout>
|
||||
<ThresholdFilter level="trace"/>
|
||||
</Console>
|
||||
|
||||
<!-- Required for printBasicInfo -->
|
||||
<Console name="Console-Appender-Println" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%msg%n%throwable{0}" />
|
||||
<PatternLayout pattern="%msg%n%throwable{short.message}" />
|
||||
</Console>
|
||||
|
||||
<!-- 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"
|
||||
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>
|
||||
<TimeBasedTriggeringPolicy/>
|
||||
@ -66,7 +78,7 @@
|
||||
<Loggers>
|
||||
<Root level="${defaultLogLevel}">
|
||||
<AppenderRef ref="Console-Appender" level="${consoleLogLevel}"/>
|
||||
<AppenderRef ref="RollingFile-Appender" />
|
||||
<AppenderRef ref="RollingFile-Appender" level="${fileLogLevel}"/>
|
||||
</Root>
|
||||
<Logger name="BasicInfo" additivity="false">
|
||||
<AppenderRef ref="Console-Appender-Println"/>
|
||||
|
@ -7,6 +7,8 @@ release, see :doc:`upgrade-notes`.
|
||||
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
|
||||
|
||||
* 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.
|
||||
|
||||
|
||||
``validate-configuration``: Validates the actual configuration without starting the node.
|
||||
|
||||
.. _enabling-remote-debugging:
|
||||
|
||||
Enabling remote debugging
|
||||
|
@ -72,6 +72,7 @@ dependencies {
|
||||
compile project(':client:rpc')
|
||||
compile project(':tools:shell')
|
||||
compile project(':tools:cliutils')
|
||||
compile project(':common-validation')
|
||||
|
||||
// Log4J: logging framework (with SLF4J bindings)
|
||||
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.ConfigFactory
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.node.services.config.ConfigHelper
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.NodeConfigurationImpl
|
||||
import net.corda.node.services.config.parseAsNodeConfiguration
|
||||
import net.corda.nodeapi.internal.config.UnknownConfigKeysPolicy
|
||||
import picocli.CommandLine.Option
|
||||
@ -39,20 +37,9 @@ open class SharedNodeCmdLineOptions {
|
||||
)
|
||||
var devMode: Boolean? = null
|
||||
|
||||
open fun loadConfig(): NodeConfiguration {
|
||||
return getRawConfig().parseAsNodeConfiguration(unknownConfigKeysPolicy::handle)
|
||||
}
|
||||
open fun parseConfiguration(configuration: Config): NodeConfiguration = configuration.parseAsNodeConfiguration(unknownConfigKeysPolicy::handle)
|
||||
|
||||
protected fun getRawConfig(): Config {
|
||||
val rawConfig = ConfigHelper.loadConfig(
|
||||
baseDirectory,
|
||||
configFile
|
||||
)
|
||||
if (devMode == true) {
|
||||
println("Config:\n${rawConfig.root().render(ConfigRenderOptions.defaults())}")
|
||||
}
|
||||
return rawConfig
|
||||
}
|
||||
open fun rawConfiguration(): Config = ConfigHelper.loadConfig(baseDirectory, configFile)
|
||||
|
||||
fun copyFrom(other: SharedNodeCmdLineOptions) {
|
||||
baseDirectory = other.baseDirectory
|
||||
@ -63,8 +50,8 @@ open class SharedNodeCmdLineOptions {
|
||||
}
|
||||
|
||||
class InitialRegistrationCmdLineOptions : SharedNodeCmdLineOptions() {
|
||||
override fun loadConfig(): NodeConfiguration {
|
||||
return getRawConfig().parseAsNodeConfiguration(unknownConfigKeysPolicy::handle).also { config ->
|
||||
override fun parseConfiguration(configuration: Config): NodeConfiguration {
|
||||
return super.parseConfiguration(configuration).also { config ->
|
||||
require(!config.devMode) { "Registration cannot occur in development mode" }
|
||||
require(config.compatibilityZoneURL != null || config.networkServices != null) {
|
||||
"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
|
||||
|
||||
override fun loadConfig(): NodeConfiguration {
|
||||
val rawConfig = ConfigHelper.loadConfig(
|
||||
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 ->
|
||||
override fun parseConfiguration(configuration: Config): NodeConfiguration {
|
||||
return super.parseConfiguration(configuration).also { config ->
|
||||
if (isRegistration) {
|
||||
require(!config.devMode) { "Registration cannot occur in development mode" }
|
||||
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
|
||||
|
||||
import com.typesafe.config.ConfigException
|
||||
import io.netty.channel.unix.Errors
|
||||
import net.corda.cliutils.*
|
||||
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.cordapp.MultipleCordappsForFlowException
|
||||
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.shouldStartLocalShell
|
||||
import net.corda.node.services.config.shouldStartSSHDaemon
|
||||
import net.corda.node.utilities.registration.NodeRegistrationException
|
||||
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.DatabaseIncompatibleException
|
||||
import net.corda.tools.shell.InteractiveShell
|
||||
@ -57,15 +56,18 @@ abstract class NodeCliCommand(alias: String, description: String, val startup: N
|
||||
/** Main corda entry point. */
|
||||
open class NodeStartupCli : CordaCliWrapper("corda", "Runs a Corda Node") {
|
||||
open val startup = NodeStartup()
|
||||
@Mixin
|
||||
val cmdLineOptions = NodeCmdLineOptions()
|
||||
|
||||
private val networkCacheCli by lazy { ClearNetworkCacheCli(startup) }
|
||||
private val justGenerateNodeInfoCli by lazy { GenerateNodeInfoCli(startup) }
|
||||
private val justGenerateRpcSslCertsCli by lazy { GenerateRpcSslCertsCli(startup) }
|
||||
private val initialRegistrationCli by lazy { InitialRegistrationCli(startup) }
|
||||
private val validateConfigurationCli by lazy { ValidateConfigurationCli() }
|
||||
|
||||
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 {
|
||||
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. */
|
||||
@ -140,13 +139,7 @@ open class NodeStartup : NodeStartupLogging {
|
||||
Node.printBasicNodeInfo(LOGS_CAN_BE_FOUND_IN_STRING, System.getProperty("log-path"))
|
||||
|
||||
// Step 5. Load and validate node configuration.
|
||||
val configuration = (attempt { cmdLineOptions.loadConfig() }.doOnException(handleConfigurationLoadingError(cmdLineOptions.configFile)) as? Try.Success)?.let(Try.Success<NodeConfiguration>::value)
|
||||
?: 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
|
||||
}
|
||||
val configuration = cmdLineOptions.nodeConfiguration().doOnErrors { errors -> logConfigurationErrors(errors, cmdLineOptions.configFile) }.optional ?: return ExitCodes.FAILURE
|
||||
|
||||
// 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
|
||||
@ -430,23 +423,6 @@ interface NodeStartupLogging {
|
||||
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) {
|
||||
|
@ -5,7 +5,7 @@ import net.corda.node.internal.NodeCliCommand
|
||||
import net.corda.node.internal.NodeStartup
|
||||
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 {
|
||||
return startup.initialiseAndRun(cmdLineOptions, object: RunAfterNodeInitialisation {
|
||||
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.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 {
|
||||
return startup.initialiseAndRun(cmdLineOptions, object : RunAfterNodeInitialisation {
|
||||
override fun run(node: Node) {
|
||||
|
@ -13,7 +13,7 @@ import net.corda.node.utilities.saveToTrustStore
|
||||
import java.io.Console
|
||||
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 {
|
||||
return startup.initialiseAndRun(cmdLineOptions, GenerateRpcSslCerts())
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import picocli.CommandLine.Option
|
||||
import java.io.File
|
||||
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."])
|
||||
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}"
|
||||
)
|
||||
debugPort?.let {
|
||||
systemProperties += "log4j2.level" to "debug"
|
||||
systemProperties += "log4j2.debug" to "true"
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user