mirror of
https://github.com/corda/corda.git
synced 2025-06-18 07:08:15 +00:00
[CORDA-926]: Parsing NodeConfiguration will now fail if unknown properties are present. (#2484)
This commit is contained in:
committed by
GitHub
parent
754b87d547
commit
b580a2ac30
@ -1,4 +1,4 @@
|
|||||||
gradlePluginsVersion=4.0.2
|
gradlePluginsVersion=4.0.3
|
||||||
kotlinVersion=1.2.20
|
kotlinVersion=1.2.20
|
||||||
platformVersion=2
|
platformVersion=2
|
||||||
guavaVersion=21.0
|
guavaVersion=21.0
|
||||||
|
@ -7,6 +7,12 @@ from the previous milestone release.
|
|||||||
UNRELEASED
|
UNRELEASED
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
* Parsing of ``NodeConfiguration`` will now fail if unknown configuration keys are found.
|
||||||
|
|
||||||
|
* The web server now has its own ``web-server.conf`` file, separate from ``node.conf``.
|
||||||
|
|
||||||
|
* Introduced a placeholder for custom properties within ``node.conf``; the property key is "custom".
|
||||||
|
|
||||||
* Added ``NetworkMapCache.getNodesByLegalName`` for querying nodes belonging to a distributed service such as a notary cluster
|
* Added ``NetworkMapCache.getNodesByLegalName`` for querying nodes belonging to a distributed service such as a notary cluster
|
||||||
where they all share a common identity. ``NetworkMapCache.getNodeByLegalName`` has been tightened to throw if more than
|
where they all share a common identity. ``NetworkMapCache.getNodeByLegalName`` has been tightened to throw if more than
|
||||||
one node with the legal name is found.
|
one node with the legal name is found.
|
||||||
|
@ -94,16 +94,6 @@ absolute path to the node's base directory.
|
|||||||
:security: Contains various nested fields controlling user authentication/authorization, in particular for RPC accesses. See
|
:security: Contains various nested fields controlling user authentication/authorization, in particular for RPC accesses. See
|
||||||
:doc:`clientrpc` for details.
|
:doc:`clientrpc` for details.
|
||||||
|
|
||||||
:webAddress: The host and port on which the webserver will listen if it is started. This is not used by the node itself.
|
|
||||||
|
|
||||||
.. note:: If HTTPS is enabled then the browser security checks will require that the accessing url host name is one
|
|
||||||
of either the machine name, fully qualified machine name, or server IP address to line up with the Subject Alternative
|
|
||||||
Names contained within the development certificates. This is addition to requiring the ``/config/dev/corda_dev_ca.cer``
|
|
||||||
root certificate be installed as a Trusted CA.
|
|
||||||
|
|
||||||
.. note:: The driver will not automatically create a webserver instance, but the Cordformation will. If this field
|
|
||||||
is present the web server will start.
|
|
||||||
|
|
||||||
:notary: Optional configuration object which if present configures the node to run as a notary. If part of a Raft or BFT SMaRt
|
:notary: Optional configuration object which if present configures the node to run as a notary. If part of a Raft or BFT SMaRt
|
||||||
cluster then specify ``raft`` or ``bftSMaRt`` respectively as described below. If a single node notary then omit both.
|
cluster then specify ``raft`` or ``bftSMaRt`` respectively as described below. If a single node notary then omit both.
|
||||||
|
|
||||||
@ -180,6 +170,7 @@ absolute path to the node's base directory.
|
|||||||
:attachmentCacheBound: Optionally specify how many attachments should be cached locally. Note that this includes only the key and
|
:attachmentCacheBound: Optionally specify how many attachments should be cached locally. Note that this includes only the key and
|
||||||
metadata, the content is cached separately and can be loaded lazily. Defaults to 1024.
|
metadata, the content is cached separately and can be loaded lazily. Defaults to 1024.
|
||||||
|
|
||||||
|
|
||||||
Examples
|
Examples
|
||||||
--------
|
--------
|
||||||
|
|
||||||
@ -202,9 +193,72 @@ Simple notary configuration file:
|
|||||||
address : "my-corda-node:10003"
|
address : "my-corda-node:10003"
|
||||||
adminAddress : "my-corda-node:10004"
|
adminAddress : "my-corda-node:10004"
|
||||||
}
|
}
|
||||||
webAddress : "localhost:12347"
|
|
||||||
notary : {
|
notary : {
|
||||||
validating : false
|
validating : false
|
||||||
}
|
}
|
||||||
devMode : true
|
devMode : true
|
||||||
compatibilityZoneURL : "https://cz.corda.net"
|
compatibilityZoneURL : "https://cz.corda.net"
|
||||||
|
|
||||||
|
An example ``web-server.conf`` file is as follow:
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
|
||||||
|
myLegalName : "O=Notary Service,OU=corda,L=London,C=GB"
|
||||||
|
keyStorePassword : "cordacadevpass"
|
||||||
|
trustStorePassword : "trustpass"
|
||||||
|
rpcSettings = {
|
||||||
|
useSsl = false
|
||||||
|
standAloneBroker = false
|
||||||
|
address : "my-corda-node:10003"
|
||||||
|
adminAddress : "my-corda-node:10004"
|
||||||
|
}
|
||||||
|
webAddress : "localhost:12347",
|
||||||
|
rpcUsers : [{ username=user1, password=letmein, permissions=[ StartFlow.net.corda.protocols.CashProtocol ] }]
|
||||||
|
|
||||||
|
Fields
|
||||||
|
------
|
||||||
|
|
||||||
|
The available config fields are listed below. ``baseDirectory`` is available as a substitution value, containing the absolute
|
||||||
|
path to the node's base directory.
|
||||||
|
|
||||||
|
:myLegalName: The legal identity of the node acts as a human readable alias to the node's public key and several demos use
|
||||||
|
this to lookup the NodeInfo.
|
||||||
|
|
||||||
|
:keyStorePassword: The password to unlock the KeyStore file (``<workspace>/certificates/sslkeystore.jks``) containing the
|
||||||
|
node certificate and private key.
|
||||||
|
|
||||||
|
.. note:: This is the non-secret value for the development certificates automatically generated during the first node run.
|
||||||
|
Longer term these keys will be managed in secure hardware devices.
|
||||||
|
|
||||||
|
:trustStorePassword: The password to unlock the Trust store file (``<workspace>/certificates/truststore.jks``) containing
|
||||||
|
the Corda network root certificate. This is the non-secret value for the development certificates automatically
|
||||||
|
generated during the first node run.
|
||||||
|
|
||||||
|
.. note:: Longer term these keys will be managed in secure hardware devices.
|
||||||
|
|
||||||
|
:rpcSettings: Options for the RPC server.
|
||||||
|
|
||||||
|
:useSsl: (optional) boolean, indicates whether the node should require clients to use SSL for RPC connections, defaulted to ``false``.
|
||||||
|
:standAloneBroker: (optional) boolean, indicates whether the node will connect to a standalone broker for RPC, defaulted to ``false``.
|
||||||
|
:address: (optional) host and port for the RPC server binding, if any.
|
||||||
|
:adminAddress: (optional) host and port for the RPC admin binding (only required when ``useSsl`` is ``false``, because the node connects to Artemis using SSL to ensure admin privileges are not accessible outside the node).
|
||||||
|
:ssl: (optional) SSL settings for the RPC client.
|
||||||
|
|
||||||
|
:keyStorePassword: password for the key store.
|
||||||
|
:trustStorePassword: password for the trust store.
|
||||||
|
:certificatesDirectory: directory in which the stores will be searched, unless absolute paths are provided.
|
||||||
|
:sslKeystore: absolute path to the ssl key store, defaulted to ``certificatesDirectory / "sslkeystore.jks"``.
|
||||||
|
:trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``.
|
||||||
|
:trustStoreFile: absolute path to the trust store, defaulted to ``certificatesDirectory / "truststore.jks"``.
|
||||||
|
|
||||||
|
:webAddress: The host and port on which the webserver will listen if it is started. This is not used by the node itself.
|
||||||
|
|
||||||
|
:rpcUsers: A list of users who are authorised to access the RPC system. Each user in the list is a config object with the
|
||||||
|
following fields:
|
||||||
|
|
||||||
|
:username: Username consisting only of word characters (a-z, A-Z, 0-9 and _)
|
||||||
|
:password: The password
|
||||||
|
:permissions: A list of permissions for starting flows via RPC. To give the user the permission to start the flow
|
||||||
|
``foo.bar.FlowClass``, add the string ``StartFlow.foo.bar.FlowClass`` to the list. If the list
|
||||||
|
contains the string ``ALL``, the user can start any flow via RPC. This value is intended for administrator
|
||||||
|
users and for development.
|
@ -2,5 +2,3 @@ myLegalName : "O=Notary Service,OU=corda,L=London,C=GB"
|
|||||||
keyStorePassword : "cordacadevpass"
|
keyStorePassword : "cordacadevpass"
|
||||||
trustStorePassword : "trustpass"
|
trustStorePassword : "trustpass"
|
||||||
p2pAddress : "my-network-map:10000"
|
p2pAddress : "my-network-map:10000"
|
||||||
webAddress : "localhost:10001"
|
|
||||||
sshdAddress : "localhost:10002"
|
|
||||||
|
@ -14,7 +14,6 @@ rpcSettings = {
|
|||||||
address : "my-corda-node:10003"
|
address : "my-corda-node:10003"
|
||||||
adminAddress : "my-corda-node:10004"
|
adminAddress : "my-corda-node:10004"
|
||||||
}
|
}
|
||||||
webAddress : "localhost:10004"
|
|
||||||
rpcUsers : [
|
rpcUsers : [
|
||||||
{ username=user1, password=letmein, permissions=[ StartFlow.net.corda.protocols.CashProtocol ] }
|
{ username=user1, password=letmein, permissions=[ StartFlow.net.corda.protocols.CashProtocol ] }
|
||||||
]
|
]
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
myLegalName : "O=Bank A,L=London,C=GB"
|
myLegalName : "O=Bank A,L=London,C=GB"
|
||||||
p2pAddress : "my-corda-node:10002"
|
p2pAddress : "my-corda-node:10002"
|
||||||
webAddress : "localhost:10003"
|
|
||||||
verifierType: "OutOfProcess"
|
verifierType: "OutOfProcess"
|
||||||
|
@ -12,7 +12,7 @@ class CashConfigDataFlowTest {
|
|||||||
@Test
|
@Test
|
||||||
fun `issuable currencies are read in from node config`() {
|
fun `issuable currencies are read in from node config`() {
|
||||||
driver {
|
driver {
|
||||||
val node = startNode(customOverrides = mapOf("issuableCurrencies" to listOf("EUR", "USD"))).getOrThrow()
|
val node = startNode(customOverrides = mapOf("custom" to mapOf("issuableCurrencies" to listOf("EUR", "USD")))).getOrThrow()
|
||||||
val config = node.rpc.startFlow(::CashConfigDataFlow).returnValue.getOrThrow()
|
val config = node.rpc.startFlow(::CashConfigDataFlow).returnValue.getOrThrow()
|
||||||
assertThat(config.issuableCurrencies).containsExactly(EUR, USD)
|
assertThat(config.issuableCurrencies).containsExactly(EUR, USD)
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import net.corda.finance.EUR
|
|||||||
import net.corda.finance.GBP
|
import net.corda.finance.GBP
|
||||||
import net.corda.finance.USD
|
import net.corda.finance.USD
|
||||||
import net.corda.finance.flows.ConfigHolder.Companion.supportedCurrencies
|
import net.corda.finance.flows.ConfigHolder.Companion.supportedCurrencies
|
||||||
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -35,13 +36,20 @@ class ConfigHolder(services: AppServiceHub) : SingletonSerializeAsToken() {
|
|||||||
.let { it.javaClass.getMethod("getConfiguration").apply { isAccessible = true }.invoke(it) }
|
.let { it.javaClass.getMethod("getConfiguration").apply { isAccessible = true }.invoke(it) }
|
||||||
.let { it.javaClass.getMethod("getBaseDirectory").apply { isAccessible = true }.invoke(it)}
|
.let { it.javaClass.getMethod("getBaseDirectory").apply { isAccessible = true }.invoke(it)}
|
||||||
.let { it.javaClass.getMethod("toString").apply { isAccessible = true }.invoke(it) as String }
|
.let { it.javaClass.getMethod("toString").apply { isAccessible = true }.invoke(it) as String }
|
||||||
|
|
||||||
|
var issuableCurrenciesValue: List<Currency>
|
||||||
|
try {
|
||||||
val config = (baseDirectory / "node.conf").read { ConfigFactory.parseReader(it.reader()) }
|
val config = (baseDirectory / "node.conf").read { ConfigFactory.parseReader(it.reader()) }
|
||||||
if (config.hasPath("issuableCurrencies")) {
|
if (config.hasPath("custom.issuableCurrencies")) {
|
||||||
issuableCurrencies = config.getStringList("issuableCurrencies").map { Currency.getInstance(it) }
|
issuableCurrenciesValue = config.getStringList("custom.issuableCurrencies").map { Currency.getInstance(it) }
|
||||||
require(supportedCurrencies.containsAll(issuableCurrencies))
|
require(supportedCurrencies.containsAll(issuableCurrenciesValue))
|
||||||
} else {
|
} else {
|
||||||
issuableCurrencies = emptyList()
|
issuableCurrenciesValue = emptyList()
|
||||||
}
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
issuableCurrenciesValue = emptyList()
|
||||||
|
}
|
||||||
|
issuableCurrencies = issuableCurrenciesValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ import kotlin.reflect.jvm.jvmErasure
|
|||||||
@Target(AnnotationTarget.PROPERTY)
|
@Target(AnnotationTarget.PROPERTY)
|
||||||
annotation class OldConfig(val value: String)
|
annotation class OldConfig(val value: String)
|
||||||
|
|
||||||
|
const val CUSTOM_NODE_PROPERTIES_ROOT = "custom"
|
||||||
|
|
||||||
// TODO Move other config parsing to use parseAs and remove this
|
// TODO Move other config parsing to use parseAs and remove this
|
||||||
operator fun <T : Any> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
|
operator fun <T : Any> Config.getValue(receiver: Any, metadata: KProperty<*>): T {
|
||||||
return getValueInternal(metadata.name, metadata.returnType)
|
return getValueInternal(metadata.name, metadata.returnType)
|
||||||
@ -37,9 +39,24 @@ operator fun <T : Any> Config.getValue(receiver: Any, metadata: KProperty<*>): T
|
|||||||
fun <T : Any> Config.parseAs(clazz: KClass<T>): T {
|
fun <T : Any> Config.parseAs(clazz: KClass<T>): T {
|
||||||
require(clazz.isData) { "Only Kotlin data classes can be parsed. Offending: ${clazz.qualifiedName}" }
|
require(clazz.isData) { "Only Kotlin data classes can be parsed. Offending: ${clazz.qualifiedName}" }
|
||||||
val constructor = clazz.primaryConstructor!!
|
val constructor = clazz.primaryConstructor!!
|
||||||
val args = constructor.parameters
|
val parameters = constructor.parameters
|
||||||
.filterNot { it.isOptional && !hasPath(it.name!!) }
|
val parameterNames = parameters.flatMap { param ->
|
||||||
.associateBy({ it }) { param ->
|
mutableSetOf<String>().apply {
|
||||||
|
param.name?.let(this::add)
|
||||||
|
clazz.memberProperties.singleOrNull { it.name == param.name }?.let { matchingProperty ->
|
||||||
|
matchingProperty.annotations.filterIsInstance<OldConfig>().map { it.value }.forEach { this.add(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val unknownConfigurationKeys = this.entrySet()
|
||||||
|
.mapNotNull { it.key.split(".").firstOrNull() }
|
||||||
|
.filterNot { it == CUSTOM_NODE_PROPERTIES_ROOT }
|
||||||
|
.filterNot(parameterNames::contains)
|
||||||
|
.toSortedSet()
|
||||||
|
if (unknownConfigurationKeys.isNotEmpty()) {
|
||||||
|
throw UnknownConfigurationKeysException.of(unknownConfigurationKeys)
|
||||||
|
}
|
||||||
|
val args = parameters.filterNot { it.isOptional && !hasPath(it.name!!) }.associateBy({ it }) { param ->
|
||||||
// Get the matching property for this parameter
|
// Get the matching property for this parameter
|
||||||
val property = clazz.memberProperties.first { it.name == param.name }
|
val property = clazz.memberProperties.first { it.name == param.name }
|
||||||
val path = defaultToOldPath(property)
|
val path = defaultToOldPath(property)
|
||||||
@ -48,6 +65,20 @@ fun <T : Any> Config.parseAs(clazz: KClass<T>): T {
|
|||||||
return constructor.callBy(args)
|
return constructor.callBy(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UnknownConfigurationKeysException private constructor(val unknownKeys: Set<String>) : IllegalArgumentException(message(unknownKeys)) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(unknownKeys.isNotEmpty()) { "Absence of unknown keys should not raise UnknownConfigurationKeysException." }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun of(offendingKeys: Set<String>): UnknownConfigurationKeysException = UnknownConfigurationKeysException(offendingKeys)
|
||||||
|
|
||||||
|
private fun message(offendingKeys: Set<String>) = "Unknown configuration keys: ${offendingKeys.joinToString(", ", "[", "]")}."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline fun <reified T : Any> Config.parseAs(): T = parseAs(T::class)
|
inline fun <reified T : Any> Config.parseAs(): T = parseAs(T::class)
|
||||||
|
|
||||||
fun Config.toProperties(): Properties {
|
fun Config.toProperties(): Properties {
|
||||||
|
@ -90,15 +90,17 @@ class NetworkBootstrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generateDirectoriesIfNeeded(directory: Path) {
|
private fun generateDirectoriesIfNeeded(directory: Path) {
|
||||||
val confFiles = directory.list { it.filter { it.toString().endsWith(".conf") }.toList() }
|
val confFiles = directory.list { it.filter { it.toString().endsWith("_node.conf") }.toList() }
|
||||||
|
val webServerConfFiles = directory.list { it.filter { it.toString().endsWith("_web-server.conf") }.toList() }
|
||||||
if (confFiles.isEmpty()) return
|
if (confFiles.isEmpty()) return
|
||||||
println("Node config files found in the root directory - generating node directories")
|
println("Node config files found in the root directory - generating node directories")
|
||||||
val cordaJar = extractCordaJarTo(directory)
|
val cordaJar = extractCordaJarTo(directory)
|
||||||
for (confFile in confFiles) {
|
for (confFile in confFiles) {
|
||||||
val nodeName = confFile.fileName.toString().removeSuffix(".conf")
|
val nodeName = confFile.fileName.toString().removeSuffix("_node.conf")
|
||||||
println("Generating directory for $nodeName")
|
println("Generating directory for $nodeName")
|
||||||
val nodeDir = (directory / nodeName).createDirectories()
|
val nodeDir = (directory / nodeName).createDirectories()
|
||||||
confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING)
|
confFile.moveTo(nodeDir / "node.conf", StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
webServerConfFiles.firstOrNull { directory.relativize(it).toString().removeSuffix("_web-server.conf") == nodeName }?.moveTo(nodeDir / "web-server.conf", StandardCopyOption.REPLACE_EXISTING)
|
||||||
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING)
|
Files.copy(cordaJar, (nodeDir / "corda.jar"), StandardCopyOption.REPLACE_EXISTING)
|
||||||
}
|
}
|
||||||
Files.delete(cordaJar)
|
Files.delete(cordaJar)
|
||||||
|
@ -200,6 +200,21 @@ class ConfigParsingTest {
|
|||||||
assertThat(DataWithCompanion(3).toConfig()).isEqualTo(config("value" to 3))
|
assertThat(DataWithCompanion(3).toConfig()).isEqualTo(config("value" to 3))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `unknown configuration keys raise exception`() {
|
||||||
|
|
||||||
|
// intentional typo here, parsing should throw rather than sneakily return default value
|
||||||
|
val knownKey = "mandatory"
|
||||||
|
val unknownKey = "optioal"
|
||||||
|
val configuration = config(knownKey to "hello", unknownKey to "world")
|
||||||
|
|
||||||
|
assertThatThrownBy { configuration.parseAs<TypedConfiguration>() }.isInstanceOfSatisfying(UnknownConfigurationKeysException::class.java) { exception ->
|
||||||
|
|
||||||
|
assertThat(exception.unknownKeys).contains(unknownKey)
|
||||||
|
assertThat(exception.unknownKeys).doesNotContain(knownKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType(
|
private inline fun <reified S : SingleData<V>, reified L : ListData<V>, V : Any> testPropertyType(
|
||||||
value1: V,
|
value1: V,
|
||||||
value2: V,
|
value2: V,
|
||||||
@ -243,6 +258,7 @@ class ConfigParsingTest {
|
|||||||
val values: List<T>
|
val values: List<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class TypedConfiguration(private val mandatory: String, private val optional: String = "optional")
|
||||||
data class StringData(override val value: String) : SingleData<String>
|
data class StringData(override val value: String) : SingleData<String>
|
||||||
data class StringListData(override val values: List<String>) : ListData<String>
|
data class StringListData(override val values: List<String>) : ListData<String>
|
||||||
data class StringSetData(val values: Set<String>)
|
data class StringSetData(val values: Set<String>)
|
||||||
|
@ -82,7 +82,6 @@ class AuthDBTests : NodeBasedTest() {
|
|||||||
"password" to "",
|
"password" to "",
|
||||||
"driverClassName" to "org.h2.Driver"
|
"driverClassName" to "org.h2.Driver"
|
||||||
)
|
)
|
||||||
)
|
|
||||||
),
|
),
|
||||||
"options" to mapOf(
|
"options" to mapOf(
|
||||||
"cache" to mapOf(
|
"cache" to mapOf(
|
||||||
@ -92,6 +91,7 @@ class AuthDBTests : NodeBasedTest() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig)
|
node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig)
|
||||||
client = CordaRPCClient(node.internals.configuration.rpcOptions.address!!)
|
client = CordaRPCClient(node.internals.configuration.rpcOptions.address!!)
|
||||||
|
@ -100,7 +100,7 @@ public class CordaCaplet extends Capsule {
|
|||||||
// Read JVM args from the config if specified, else leave alone.
|
// Read JVM args from the config if specified, else leave alone.
|
||||||
List<String> jvmArgs = new ArrayList<>((List<String>) super.attribute(attr));
|
List<String> jvmArgs = new ArrayList<>((List<String>) super.attribute(attr));
|
||||||
try {
|
try {
|
||||||
List<String> configJvmArgs = nodeConfig.getStringList("jvmArgs");
|
List<String> configJvmArgs = nodeConfig.getStringList("custom.jvmArgs");
|
||||||
jvmArgs.clear();
|
jvmArgs.clear();
|
||||||
jvmArgs.addAll(configJvmArgs);
|
jvmArgs.addAll(configJvmArgs);
|
||||||
log(LOG_VERBOSE, "Configured JVM args = " + jvmArgs);
|
log(LOG_VERBOSE, "Configured JVM args = " + jvmArgs);
|
||||||
|
@ -17,6 +17,7 @@ import net.corda.node.shell.InteractiveShell
|
|||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
import net.corda.node.utilities.registration.NetworkRegistrationHelper
|
||||||
import net.corda.nodeapi.internal.addShutdownHook
|
import net.corda.nodeapi.internal.addShutdownHook
|
||||||
|
import net.corda.nodeapi.internal.config.UnknownConfigurationKeysException
|
||||||
import org.fusesource.jansi.Ansi
|
import org.fusesource.jansi.Ansi
|
||||||
import org.fusesource.jansi.AnsiConsole
|
import org.fusesource.jansi.AnsiConsole
|
||||||
import org.slf4j.bridge.SLF4JBridgeHandler
|
import org.slf4j.bridge.SLF4JBridgeHandler
|
||||||
@ -86,6 +87,9 @@ open class NodeStartup(val args: Array<String>) {
|
|||||||
} else {
|
} else {
|
||||||
conf0
|
conf0
|
||||||
}
|
}
|
||||||
|
} catch (e: UnknownConfigurationKeysException) {
|
||||||
|
logger.error(e.message)
|
||||||
|
return false
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("Exception during node configuration", e)
|
logger.error("Exception during node configuration", e)
|
||||||
return false
|
return false
|
||||||
|
@ -150,7 +150,9 @@ data class NodeConfigurationImpl(
|
|||||||
override val database: DatabaseConfig = DatabaseConfig(initialiseSchema = devMode, exportHibernateJMXStatistics = devMode),
|
override val database: DatabaseConfig = DatabaseConfig(initialiseSchema = devMode, exportHibernateJMXStatistics = devMode),
|
||||||
private val transactionCacheSizeMegaBytes: Int? = null,
|
private val transactionCacheSizeMegaBytes: Int? = null,
|
||||||
private val attachmentContentCacheSizeMegaBytes: Int? = null,
|
private val attachmentContentCacheSizeMegaBytes: Int? = null,
|
||||||
override val attachmentCacheBound: Long = NodeConfiguration.defaultAttachmentCacheBound
|
override val attachmentCacheBound: Long = NodeConfiguration.defaultAttachmentCacheBound,
|
||||||
|
// do not use or remove (breaks DemoBench together with rejection of unknown configuration keys during parsing)
|
||||||
|
private val h2port: Int = 0
|
||||||
) : NodeConfiguration {
|
) : NodeConfiguration {
|
||||||
companion object {
|
companion object {
|
||||||
private val logger = loggerFor<NodeConfigurationImpl>()
|
private val logger = loggerFor<NodeConfigurationImpl>()
|
||||||
|
@ -38,7 +38,7 @@ class BankOfCordaCordform : CordformDefinition() {
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name(BOC_NAME)
|
name(BOC_NAME)
|
||||||
extraConfig = mapOf("issuableCurrencies" to listOf("USD"))
|
extraConfig = mapOf("custom" to mapOf("issuableCurrencies" to listOf("USD")))
|
||||||
p2pPort(10005)
|
p2pPort(10005)
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
address("localhost:$BOC_RPC_PORT")
|
address("localhost:$BOC_RPC_PORT")
|
||||||
|
@ -64,8 +64,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
notary = [validating : true]
|
notary = [validating : true]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
port 10003
|
address("localhost:10003")
|
||||||
adminPort 10023
|
adminAddress("localhost:10023")
|
||||||
}
|
}
|
||||||
cordapps = ["${project(":finance").group}:finance:$corda_release_version"]
|
cordapps = ["${project(":finance").group}:finance:$corda_release_version"]
|
||||||
rpcUsers = rpcUsersList
|
rpcUsers = rpcUsersList
|
||||||
@ -75,8 +75,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
name "O=Bank A,L=London,C=GB"
|
name "O=Bank A,L=London,C=GB"
|
||||||
p2pPort 10005
|
p2pPort 10005
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
port 10006
|
address("localhost:10006")
|
||||||
adminPort 10026
|
adminAddress("localhost:10026")
|
||||||
}
|
}
|
||||||
cordapps = ["${project(":finance").group}:finance:$corda_release_version"]
|
cordapps = ["${project(":finance").group}:finance:$corda_release_version"]
|
||||||
rpcUsers = rpcUsersList
|
rpcUsers = rpcUsersList
|
||||||
@ -86,8 +86,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
name "O=Bank B,L=New York,C=US"
|
name "O=Bank B,L=New York,C=US"
|
||||||
p2pPort 10008
|
p2pPort 10008
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
port 10009
|
address("localhost:10009")
|
||||||
adminPort 10029
|
adminAddress("localhost:10029")
|
||||||
}
|
}
|
||||||
cordapps = ["${project.group}:finance:$corda_release_version"]
|
cordapps = ["${project.group}:finance:$corda_release_version"]
|
||||||
rpcUsers = rpcUsersList
|
rpcUsers = rpcUsersList
|
||||||
@ -97,8 +97,8 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
name "O=Regulator,L=Moscow,C=RU"
|
name "O=Regulator,L=Moscow,C=RU"
|
||||||
p2pPort 10011
|
p2pPort 10011
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
port 10012
|
address("localhost:10012")
|
||||||
adminPort 10032
|
adminAddress("localhost:10032")
|
||||||
}
|
}
|
||||||
cordapps = ["${project.group}:finance:$corda_release_version"]
|
cordapps = ["${project.group}:finance:$corda_release_version"]
|
||||||
cordapps = ["${project(":finance").group}:finance:$corda_release_version"]
|
cordapps = ["${project(":finance").group}:finance:$corda_release_version"]
|
||||||
|
@ -70,8 +70,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
cordapp project(':finance')
|
cordapp project(':finance')
|
||||||
extraConfig = [
|
extraConfig = [
|
||||||
|
custom: [
|
||||||
jvmArgs: ["-Xmx1g"]
|
jvmArgs: ["-Xmx1g"]
|
||||||
]
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank A,L=London,C=GB"
|
name "O=Bank A,L=London,C=GB"
|
||||||
@ -84,8 +86,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
cordapp project(':finance')
|
cordapp project(':finance')
|
||||||
rpcUsers = ext.rpcUsers
|
rpcUsers = ext.rpcUsers
|
||||||
extraConfig = [
|
extraConfig = [
|
||||||
|
custom: [
|
||||||
jvmArgs: ["-Xmx1g"]
|
jvmArgs: ["-Xmx1g"]
|
||||||
]
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank B,L=New York,C=US"
|
name "O=Bank B,L=New York,C=US"
|
||||||
@ -98,8 +102,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
cordapp project(':finance')
|
cordapp project(':finance')
|
||||||
rpcUsers = ext.rpcUsers
|
rpcUsers = ext.rpcUsers
|
||||||
extraConfig = [
|
extraConfig = [
|
||||||
|
custom: [
|
||||||
jvmArgs: ["-Xmx1g"]
|
jvmArgs: ["-Xmx1g"]
|
||||||
]
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Bank C,L=Tokyo,C=JP"
|
name "O=Bank C,L=Tokyo,C=JP"
|
||||||
@ -112,8 +118,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar']) {
|
|||||||
cordapp project(':finance')
|
cordapp project(':finance')
|
||||||
rpcUsers = ext.rpcUsers
|
rpcUsers = ext.rpcUsers
|
||||||
extraConfig = [
|
extraConfig = [
|
||||||
|
custom: [
|
||||||
jvmArgs: ["-Xmx1g"]
|
jvmArgs: ["-Xmx1g"]
|
||||||
]
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder
|
|||||||
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 com.typesafe.config.ConfigRenderOptions
|
||||||
|
import com.typesafe.config.ConfigValueFactory
|
||||||
import net.corda.client.rpc.CordaRPCClient
|
import net.corda.client.rpc.CordaRPCClient
|
||||||
import net.corda.client.rpc.internal.createCordaRPCClientWithSsl
|
import net.corda.client.rpc.internal.createCordaRPCClientWithSsl
|
||||||
import net.corda.cordform.CordformContext
|
import net.corda.cordform.CordformContext
|
||||||
@ -229,7 +230,6 @@ class DriverDSLImpl(
|
|||||||
"p2pAddress" to p2pAddress.toString(),
|
"p2pAddress" to p2pAddress.toString(),
|
||||||
"rpcSettings.address" to rpcAddress.toString(),
|
"rpcSettings.address" to rpcAddress.toString(),
|
||||||
"rpcSettings.adminAddress" to rpcAdminAddress.toString(),
|
"rpcSettings.adminAddress" to rpcAdminAddress.toString(),
|
||||||
"webAddress" to webAddress.toString(),
|
|
||||||
"useTestClock" to useTestClock,
|
"useTestClock" to useTestClock,
|
||||||
"rpcUsers" to if (users.isEmpty()) defaultRpcUserList else users.map { it.toConfig().root().unwrapped() },
|
"rpcUsers" to if (users.isEmpty()) defaultRpcUserList else users.map { it.toConfig().root().unwrapped() },
|
||||||
"verifierType" to verifierType.name
|
"verifierType" to verifierType.name
|
||||||
@ -355,13 +355,17 @@ class DriverDSLImpl(
|
|||||||
val webAddress = cordform.webAddress?.let { NetworkHostAndPort.parse(it) } ?: portAllocation.nextHostAndPort()
|
val webAddress = cordform.webAddress?.let { NetworkHostAndPort.parse(it) } ?: portAllocation.nextHostAndPort()
|
||||||
val notary = if (cordform.notary != null) mapOf("notary" to cordform.notary) else emptyMap()
|
val notary = if (cordform.notary != null) mapOf("notary" to cordform.notary) else emptyMap()
|
||||||
val rpcUsers = cordform.rpcUsers
|
val rpcUsers = cordform.rpcUsers
|
||||||
val config = NodeConfig(ConfigHelper.loadConfig(
|
|
||||||
baseDirectory = baseDirectory(name),
|
val rawConfig = cordform.config + rpcAddress + notary + mapOf(
|
||||||
allowMissingConfig = true,
|
|
||||||
configOverrides = cordform.config + rpcAddress + notary + mapOf(
|
|
||||||
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers
|
"rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers
|
||||||
)
|
)
|
||||||
))
|
val typesafe = ConfigHelper.loadConfig(
|
||||||
|
baseDirectory = baseDirectory(name),
|
||||||
|
allowMissingConfig = true,
|
||||||
|
configOverrides = rawConfig.toNodeOnly()
|
||||||
|
)
|
||||||
|
val cordaConfig = typesafe.parseAsNodeConfiguration()
|
||||||
|
val config = NodeConfig(rawConfig, cordaConfig)
|
||||||
return startNodeInternal(config, webAddress, null, "200m", localNetworkMap)
|
return startNodeInternal(config, webAddress, null, "200m", localNetworkMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,9 +388,9 @@ class DriverDSLImpl(
|
|||||||
|
|
||||||
override fun startWebserver(handle: NodeHandle, maximumHeapSize: String): CordaFuture<WebserverHandle> {
|
override fun startWebserver(handle: NodeHandle, maximumHeapSize: String): CordaFuture<WebserverHandle> {
|
||||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||||
val process = startWebserver(handle, debugPort, maximumHeapSize)
|
val process = startWebserver(handle as NodeHandleInternal, debugPort, maximumHeapSize)
|
||||||
shutdownManager.registerProcessShutdown(process)
|
shutdownManager.registerProcessShutdown(process)
|
||||||
val webReadyFuture = addressMustBeBoundFuture(executorService, (handle as NodeHandleInternal).webAddress, process)
|
val webReadyFuture = addressMustBeBoundFuture(executorService, handle.webAddress, process)
|
||||||
return webReadyFuture.map { queryWebserver(handle, process) }
|
return webReadyFuture.map { queryWebserver(handle, process) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,14 +730,12 @@ class DriverDSLImpl(
|
|||||||
* Simple holder class to capture the node configuration both as the raw [Config] object and the parsed [NodeConfiguration].
|
* Simple holder class to capture the node configuration both as the raw [Config] object and the parsed [NodeConfiguration].
|
||||||
* Keeping [Config] around is needed as the user may specify extra config options not specified in [NodeConfiguration].
|
* Keeping [Config] around is needed as the user may specify extra config options not specified in [NodeConfiguration].
|
||||||
*/
|
*/
|
||||||
private class NodeConfig(val typesafe: Config) {
|
private class NodeConfig(val typesafe: Config, val corda: NodeConfiguration = typesafe.parseAsNodeConfiguration().also { nodeConfiguration ->
|
||||||
val corda: NodeConfiguration = typesafe.parseAsNodeConfiguration().also { nodeConfiguration ->
|
|
||||||
val errors = nodeConfiguration.validate()
|
val errors = nodeConfiguration.validate()
|
||||||
if (errors.isNotEmpty()) {
|
if (errors.isNotEmpty()) {
|
||||||
throw IllegalStateException("Invalid node configuration. Errors where:${System.lineSeparator()}${errors.joinToString(System.lineSeparator())}")
|
throw IllegalStateException("Invalid node configuration. Errors where:${System.lineSeparator()}${errors.joinToString(System.lineSeparator())}")
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
internal val log = contextLogger()
|
internal val log = contextLogger()
|
||||||
@ -771,7 +773,7 @@ class DriverDSLImpl(
|
|||||||
throw IllegalStateException("No quasar agent: -javaagent:lib/quasar.jar and working directory project root might fix")
|
throw IllegalStateException("No quasar agent: -javaagent:lib/quasar.jar and working directory project root might fix")
|
||||||
}
|
}
|
||||||
// Write node.conf
|
// Write node.conf
|
||||||
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe)
|
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly())
|
||||||
// TODO pass the version in?
|
// TODO pass the version in?
|
||||||
val node = InProcessNode(config.corda, MOCK_VERSION_INFO, cordappPackages).start()
|
val node = InProcessNode(config.corda, MOCK_VERSION_INFO, cordappPackages).start()
|
||||||
val nodeThread = thread(name = config.corda.myLegalName.organisation) {
|
val nodeThread = thread(name = config.corda.myLegalName.organisation) {
|
||||||
@ -798,7 +800,7 @@ class DriverDSLImpl(
|
|||||||
"debug port is " + (debugPort ?: "not enabled") + ", " +
|
"debug port is " + (debugPort ?: "not enabled") + ", " +
|
||||||
"jolokia monitoring port is " + (monitorPort ?: "not enabled"))
|
"jolokia monitoring port is " + (monitorPort ?: "not enabled"))
|
||||||
// Write node.conf
|
// Write node.conf
|
||||||
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe)
|
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly())
|
||||||
|
|
||||||
val systemProperties = mutableMapOf(
|
val systemProperties = mutableMapOf(
|
||||||
"name" to config.corda.myLegalName,
|
"name" to config.corda.myLegalName,
|
||||||
@ -844,8 +846,9 @@ class DriverDSLImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startWebserver(handle: NodeHandle, debugPort: Int?, maximumHeapSize: String): Process {
|
private fun startWebserver(handle: NodeHandleInternal, debugPort: Int?, maximumHeapSize: String): Process {
|
||||||
val className = "net.corda.webserver.WebServer"
|
val className = "net.corda.webserver.WebServer"
|
||||||
|
writeConfig(handle.baseDirectory, "web-server.conf", handle.toWebServerConfig())
|
||||||
return ProcessUtilities.startCordaProcess(
|
return ProcessUtilities.startCordaProcess(
|
||||||
className = className, // cannot directly get class for this, so just use string
|
className = className, // cannot directly get class for this, so just use string
|
||||||
arguments = listOf("--base-directory", handle.baseDirectory.toString()),
|
arguments = listOf("--base-directory", handle.baseDirectory.toString()),
|
||||||
@ -860,6 +863,22 @@ class DriverDSLImpl(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun NodeHandleInternal.toWebServerConfig(): Config {
|
||||||
|
|
||||||
|
var config = ConfigFactory.empty()
|
||||||
|
config += "webAddress" to webAddress.toString()
|
||||||
|
config += "myLegalName" to configuration.myLegalName.toString()
|
||||||
|
config += "rpcAddress" to configuration.rpcOptions.address!!.toString()
|
||||||
|
config += "rpcUsers" to configuration.toConfig().getValue("rpcUsers")
|
||||||
|
config += "useHTTPS" to useHTTPS
|
||||||
|
config += "baseDirectory" to configuration.baseDirectory.toAbsolutePath().toString()
|
||||||
|
config += "keyStorePassword" to configuration.keyStorePassword
|
||||||
|
config += "trustStorePassword" to configuration.trustStorePassword
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
private operator fun Config.plus(property: Pair<String, Any>) = withValue(property.first, ConfigValueFactory.fromAnyRef(property.second))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the package of the caller to the driver so that it can be added to the list of packages the nodes will scan.
|
* Get the package of the caller to the driver so that it can be added to the list of packages the nodes will scan.
|
||||||
* This makes the driver automatically pick the CorDapp module that it's run from.
|
* This makes the driver automatically pick the CorDapp module that it's run from.
|
||||||
@ -1053,3 +1072,14 @@ fun writeConfig(path: Path, filename: String, config: Config) {
|
|||||||
val configString = config.root().render(ConfigRenderOptions.defaults())
|
val configString = config.root().render(ConfigRenderOptions.defaults())
|
||||||
configString.byteInputStream().copyTo(path / filename, StandardCopyOption.REPLACE_EXISTING)
|
configString.byteInputStream().copyTo(path / filename, StandardCopyOption.REPLACE_EXISTING)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Config.toNodeOnly(): Config {
|
||||||
|
|
||||||
|
return if (hasPath("webAddress")) {
|
||||||
|
withoutPath("webAddress").withoutPath("useHTTPS")
|
||||||
|
} else {
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private operator fun Config.plus(property: Pair<String, Any>) = withValue(property.first, ConfigValueFactory.fromAnyRef(property.second))
|
@ -46,17 +46,49 @@ data class NodeConfig(
|
|||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
private val useTestClock = true
|
private val useTestClock = true
|
||||||
|
|
||||||
private fun asConfig(): Config {
|
fun nodeConf(): Config {
|
||||||
|
|
||||||
val config = toConfig()
|
val basic = NodeConfigurationData(myLegalName, p2pAddress, rpcAddress, notary, h2port, rpcUsers, useTestClock, detectPublicIp).toConfig()
|
||||||
val rpcSettings = empty()
|
val rpcSettings = empty()
|
||||||
.withValue("address", ConfigValueFactory.fromAnyRef(rpcAddress.toString()))
|
.withValue("address", ConfigValueFactory.fromAnyRef(rpcAddress.toString()))
|
||||||
.withValue("adminAddress", ConfigValueFactory.fromAnyRef(rpcAdminAddress.toString()))
|
.withValue("adminAddress", ConfigValueFactory.fromAnyRef(rpcAdminAddress.toString()))
|
||||||
.root()
|
.root()
|
||||||
return config.withoutPath("rpcAddress").withoutPath("rpcAdminAddress").withValue("rpcSettings", rpcSettings)
|
return basic.withoutPath("rpcAddress").withoutPath("rpcAdminAddress").withValue("rpcSettings", rpcSettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toText(): String = asConfig().root().render(renderOptions)
|
fun webServerConf() = WebServerConfigurationData(myLegalName, rpcAddress, webAddress, rpcUsers).asConfig()
|
||||||
|
|
||||||
|
fun toNodeConfText() = nodeConf().render()
|
||||||
|
|
||||||
|
fun toWebServerConfText() = webServerConf().render()
|
||||||
|
|
||||||
|
fun serialiseAsString(): String {
|
||||||
|
|
||||||
|
return toConfig().render()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Config.render(): String = root().render(renderOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class NodeConfigurationData(
|
||||||
|
val myLegalName: CordaX500Name,
|
||||||
|
val p2pAddress: NetworkHostAndPort,
|
||||||
|
val rpcAddress: NetworkHostAndPort,
|
||||||
|
val notary: NotaryService?,
|
||||||
|
val h2port: Int,
|
||||||
|
val rpcUsers: List<User> = listOf(NodeConfig.defaultUser),
|
||||||
|
val useTestClock: Boolean,
|
||||||
|
val detectPublicIp: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
private data class WebServerConfigurationData(
|
||||||
|
val myLegalName: CordaX500Name,
|
||||||
|
val rpcAddress: NetworkHostAndPort,
|
||||||
|
val webAddress: NetworkHostAndPort,
|
||||||
|
val rpcUsers: List<User>
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun asConfig() = toConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -121,7 +121,11 @@ class NodeController(check: atRuntime = ::checkExists) : Controller() {
|
|||||||
|
|
||||||
// Write this node's configuration file into its working directory.
|
// Write this node's configuration file into its working directory.
|
||||||
val confFile = config.nodeDir / "node.conf"
|
val confFile = config.nodeDir / "node.conf"
|
||||||
Files.write(confFile, config.nodeConfig.toText().toByteArray())
|
Files.write(confFile, config.nodeConfig.toNodeConfText().toByteArray())
|
||||||
|
|
||||||
|
// Write this node's configuration file into its working directory.
|
||||||
|
val webConfFile = config.nodeDir / "web-server.conf"
|
||||||
|
Files.write(webConfFile, config.nodeConfig.toWebServerConfText().toByteArray())
|
||||||
|
|
||||||
// Execute the Corda node
|
// Execute the Corda node
|
||||||
val cordaEnv = System.getenv().toMutableMap().apply {
|
val cordaEnv = System.getenv().toMutableMap().apply {
|
||||||
|
@ -58,7 +58,7 @@ class ProfileController : Controller() {
|
|||||||
configs.forEach { config ->
|
configs.forEach { config ->
|
||||||
// Write the configuration file.
|
// Write the configuration file.
|
||||||
val nodeDir = fs.getPath(config.key).createDirectories()
|
val nodeDir = fs.getPath(config.key).createDirectories()
|
||||||
val file = Files.write(nodeDir / "node.conf", config.nodeConfig.toText().toByteArray(UTF_8))
|
val file = Files.write(nodeDir / "node.conf", config.nodeConfig.serialiseAsString().toByteArray(UTF_8))
|
||||||
log.info("Wrote: $file")
|
log.info("Wrote: $file")
|
||||||
|
|
||||||
// Write all of the non-built-in cordapps.
|
// Write all of the non-built-in cordapps.
|
||||||
|
@ -11,7 +11,7 @@ class WebServerController : Controller() {
|
|||||||
log.info("Web Server JAR: $webserverPath")
|
log.info("Web Server JAR: $webserverPath")
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun process() = jvm.processFor(webserverPath)
|
internal fun process() = jvm.processFor(webserverPath, "--config-file", "web-server.conf")
|
||||||
|
|
||||||
fun webServer() = WebServer(this)
|
fun webServer() = WebServer(this)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import net.corda.core.identity.CordaX500Name
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.node.services.config.parseAsNodeConfiguration
|
import net.corda.node.services.config.parseAsNodeConfiguration
|
||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.nodeapi.internal.config.toConfig
|
|
||||||
import net.corda.webserver.WebServerConfig
|
import net.corda.webserver.WebServerConfig
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@ -28,14 +27,14 @@ class NodeConfigTest {
|
|||||||
legalName = myLegalName,
|
legalName = myLegalName,
|
||||||
p2pPort = 10001,
|
p2pPort = 10001,
|
||||||
rpcPort = 40002,
|
rpcPort = 40002,
|
||||||
rpcAdminPort = 40003,
|
rpcAdminPort = 40005,
|
||||||
webPort = 20001,
|
webPort = 20001,
|
||||||
h2port = 30001,
|
h2port = 30001,
|
||||||
notary = NotaryService(validating = false),
|
notary = NotaryService(validating = false),
|
||||||
users = listOf(user("jenny"))
|
users = listOf(user("jenny"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val nodeConfig = config.toConfig()
|
val nodeConfig = config.nodeConf()
|
||||||
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
|
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
|
||||||
.withFallback(ConfigFactory.parseResources("reference.conf"))
|
.withFallback(ConfigFactory.parseResources("reference.conf"))
|
||||||
.resolve()
|
.resolve()
|
||||||
@ -63,7 +62,7 @@ class NodeConfigTest {
|
|||||||
users = listOf(user("jenny"))
|
users = listOf(user("jenny"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val nodeConfig = config.toConfig()
|
val nodeConfig = config.webServerConf()
|
||||||
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
|
.withValue("baseDirectory", ConfigValueFactory.fromAnyRef(baseDir.toString()))
|
||||||
.withFallback(ConfigFactory.parseResources("web-reference.conf"))
|
.withFallback(ConfigFactory.parseResources("web-reference.conf"))
|
||||||
.resolve()
|
.resolve()
|
||||||
|
@ -70,9 +70,9 @@ class ExplorerSimulation(private val options: OptionSet) {
|
|||||||
val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB")
|
val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB")
|
||||||
val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US")
|
val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US")
|
||||||
val issuerGBP = startNode(providedName = ukBankName, rpcUsers = listOf(manager),
|
val issuerGBP = startNode(providedName = ukBankName, rpcUsers = listOf(manager),
|
||||||
customOverrides = mapOf("issuableCurrencies" to listOf("GBP")))
|
customOverrides = mapOf("custom" to mapOf("issuableCurrencies" to listOf("GBP"))))
|
||||||
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = listOf(manager),
|
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = listOf(manager),
|
||||||
customOverrides = mapOf("issuableCurrencies" to listOf("USD")))
|
customOverrides = mapOf("custom" to mapOf("issuableCurrencies" to listOf("USD"))))
|
||||||
|
|
||||||
notaryNode = defaultNotaryNode.get()
|
notaryNode = defaultNotaryNode.get()
|
||||||
aliceNode = alice.get()
|
aliceNode = alice.get()
|
||||||
|
@ -25,7 +25,7 @@ class ArgsParser {
|
|||||||
private val configFileArg = optionParser
|
private val configFileArg = optionParser
|
||||||
.accepts("config-file", "The path to the config file")
|
.accepts("config-file", "The path to the config file")
|
||||||
.withRequiredArg()
|
.withRequiredArg()
|
||||||
.defaultsTo("node.conf")
|
.defaultsTo("web-server.conf")
|
||||||
private val loggerLevel = optionParser
|
private val loggerLevel = optionParser
|
||||||
.accepts("logging-level", "Enable logging at this level and higher")
|
.accepts("logging-level", "Enable logging at this level and higher")
|
||||||
.withRequiredArg()
|
.withRequiredArg()
|
||||||
|
Reference in New Issue
Block a user