From 926429647de6302451869a03a6cb87e9c4780a65 Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Tue, 19 Nov 2019 17:51:52 +0000 Subject: [PATCH] CORDA-3307: Fix for underscore variables (#5682) * Revert "Revert "CORDA-3307 - add support for environment variables in linux (#5523)" (#5643)" This reverts commit 03ab258fc2d0f40badd73f78c81e5ec6badf2940. * Env variables with underscore are now validated using schema validation and checking for unknown key errors. * Resolving comments from PR review. * Fix for deprecated import. * Reworked logic according to PR review. * Resolved bad string parsing problems where the json structure could be broken if some symbols were included in the key or value. --- docs/source/changelog.rst | 3 + docs/source/corda-configuration-file.rst | 7 + .../net/corda/node/NodeConfigParsingTests.kt | 110 +++++++++++ .../corda/node/NodeStartupPerformanceTests.kt | 2 +- .../node/services/config/ConfigUtilities.kt | 50 ++++- .../services/config/ShadowingException.kt | 7 + .../kotlin/net/corda/testing/driver/Driver.kt | 14 +- .../testing/node/internal/DriverDSLImpl.kt | 171 +++++++++--------- .../testing/node/internal/ProcessUtilities.kt | 19 +- .../corda/testing/node/internal/RPCDriver.kt | 4 +- 10 files changed, 292 insertions(+), 95 deletions(-) create mode 100644 node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt create mode 100644 node/src/main/kotlin/net/corda/node/services/config/ShadowingException.kt diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index c1669584de..5ee34e8bc1 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -88,6 +88,9 @@ Unreleased Note that it's a responsibility of a client application to handle RPC reconnection in case this happens. See :ref:`setting_jvm_args` and :ref:`memory_usage_and_tuning` for further details. +* Environment variables and system properties can now be provided with underscore separators instead of dots. Neither are case sensitive. + See :ref:`overriding config values ` for more information. + .. _changelog_v4.1: Version 4.1 diff --git a/docs/source/corda-configuration-file.rst b/docs/source/corda-configuration-file.rst index 6cbbc141d9..e44b92b10c 100644 --- a/docs/source/corda-configuration-file.rst +++ b/docs/source/corda-configuration-file.rst @@ -39,6 +39,8 @@ To alter this behaviour, the ``on-unknown-config-keys`` command-line argument ca Overriding values from node.conf -------------------------------- +.. _corda_configuration_file_overriding_config: + Environment variables For example: ``${NODE_TRUST_STORE_PASSWORD}`` would be replaced by the contents of environment variable ``NODE_TRUST_STORE_PASSWORD`` (see: :ref:`hiding-sensitive-data` section). @@ -54,6 +56,11 @@ JVM options .. note:: If the same field is overriden by both an environment variable and system property, the system property takes precedence. +.. note:: Underscores can be used in instead of dots. For example overriding the ``p2pAddress`` with an environment variable can be done + by specifying ``CORDA_P2PADDRESS=host:port``. Variables and properties are not case sensitive. Corda will warn you if a variable + prefixed with ``CORDA`` cannot be mapped to a valid property. Shadowing occurs when two properties + of the same type with the same key are defined. For example having ``CORDA_P2PADDRESS=host:port`` and ``corda_p2paddress=host1:port1`` + will raise an exception on startup. This is to prevent hard to spot mistakes. Configuration file fields ------------------------- diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt new file mode 100644 index 0000000000..8085b9c3d6 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt @@ -0,0 +1,110 @@ +package net.corda.node + +import net.corda.core.utilities.getOrThrow +import net.corda.node.logging.logFile +import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.NodeParameters +import net.corda.testing.driver.driver +import net.corda.testing.driver.internal.incrementalPortAllocation +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.Test +import org.junit.Assert.assertTrue + + +class NodeConfigParsingTests { + + @Test + fun `config is overriden by underscore variable`() { + val portAllocator = incrementalPortAllocation() + val sshPort = portAllocator.nextPort() + + driver(DriverParameters( + environmentVariables = mapOf("corda_sshd_port" to sshPort.toString()), + startNodesInProcess = false, + portAllocation = portAllocator)) { + val hasSsh = startNode().get() + .logFile() + .readLines() + .filter { it.contains("SSH server listening on port") } + .any { it.contains(sshPort.toString()) } + assertTrue(hasSsh) + } + } + + @Test + fun `config is overriden by case insensitive underscore variable`() { + val portAllocator = incrementalPortAllocation() + val sshPort = portAllocator.nextPort() + + driver(DriverParameters( + environmentVariables = mapOf("CORDA_sshd_port" to sshPort.toString()), + startNodesInProcess = false, + portAllocation = portAllocator)) { + val hasSsh = startNode().get() + .logFile() + .readLines() + .filter { it.contains("SSH server listening on port") } + .any { it.contains(sshPort.toString()) } + assertTrue(hasSsh) + } + } + + @Test + fun `config is overriden by case insensitive dot variable`() { + val portAllocator = incrementalPortAllocation() + val sshPort = portAllocator.nextPort() + + driver(DriverParameters( + environmentVariables = mapOf("CORDA.sshd.port" to sshPort.toString(), + "corda.devMode" to true.toString()), + startNodesInProcess = false, + portAllocation = portAllocator)) { + val hasSsh = startNode(NodeParameters()).get() + .logFile() + .readLines() + .filter { it.contains("SSH server listening on port") } + .any { it.contains(sshPort.toString()) } + assertTrue(hasSsh) + } + } + + @Test + fun `shadowing is forbidden`() { + val portAllocator = incrementalPortAllocation() + val sshPort = portAllocator.nextPort() + + driver(DriverParameters( + environmentVariables = mapOf( + "CORDA_sshd_port" to sshPort.toString(), + "corda.sshd.port" to sshPort.toString()), + startNodesInProcess = false, + portAllocation = portAllocator, + notarySpecs = emptyList())) { + + assertThatThrownBy { + startNode().getOrThrow() + } + } + } + + @Test + fun `bad keys are ignored and warned for`() { + val portAllocator = incrementalPortAllocation() + driver(DriverParameters( + environmentVariables = mapOf( + "corda_bad_key" to "2077"), + startNodesInProcess = false, + portAllocation = portAllocator, + notarySpecs = emptyList())) { + + val hasWarning = startNode() + .getOrThrow() + .logFile() + .readLines() + .any { + it.contains("(property or environment variable) cannot be mapped to an existing Corda") + } + assertTrue(hasWarning) + } + } +} diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt index a9f2586013..12c434a69d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt @@ -24,4 +24,4 @@ class NodeStartupPerformanceTests { println(times.map { it / 1_000_000.0 }) } } -} +} \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index ab316aadd7..2c85154148 100644 --- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt +++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt @@ -4,10 +4,13 @@ import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigParseOptions import net.corda.cliutils.CordaSystemUtils +import net.corda.common.configuration.parsing.internal.Configuration import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.exists +import net.corda.node.internal.Node +import net.corda.node.services.config.schema.v1.V1NodeConfigurationSpec import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_PASS import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier import net.corda.nodeapi.internal.config.MutualSslConfiguration @@ -27,6 +30,7 @@ operator fun Config.plus(overrides: Map): Config = ConfigFactory.p object ConfigHelper { private const val CORDA_PROPERTY_PREFIX = "corda." + private const val UPPERCASE_PROPERTY_PREFIX = "CORDA." private val log = LoggerFactory.getLogger(javaClass) fun loadConfig(baseDirectory: Path, @@ -68,10 +72,48 @@ object ConfigHelper { } private fun Config.cordaEntriesOnly(): Config { - return ConfigFactory.parseMap(toProperties() - .filterKeys { (it as String).startsWith(CORDA_PROPERTY_PREFIX) } - .mapKeys { (it.key as String).removePrefix(CORDA_PROPERTY_PREFIX) } - ) + val cordaPropOccurrences = mutableSetOf() + val badKeyConversions = mutableSetOf() + + return ConfigFactory.parseMap( + toProperties() + .mapKeys { + var newKey = (it.key as String) + .replace('_', '.') + .replace(UPPERCASE_PROPERTY_PREFIX, CORDA_PROPERTY_PREFIX) + + if (!newKey.startsWith(CORDA_PROPERTY_PREFIX)) { + return@mapKeys newKey + } + + newKey = newKey.substring(CORDA_PROPERTY_PREFIX.length) + + if (cordaPropOccurrences.contains(newKey)) + { + throw ShadowingException(it.key.toString(), newKey) + } + + cordaPropOccurrences.add(newKey) + + newKey.let { key -> + val cfg = ConfigFactory.parseMap(mapOf(key to it.value)) + val result = V1NodeConfigurationSpec.validate(cfg, Configuration.Validation.Options(strict = true)) + + val isInvalidProperty = result.errors.any { err -> err is Configuration.Validation.Error.Unknown } + if (isInvalidProperty) { + Node.printWarning( + "${it.key} (property or environment variable) cannot be mapped to an existing Corda" + + " config property and thus won't be used as a config override!" + + " It won't be passed as a config override! If that was the intention " + + " double check the spelling and ensure there is such config key.") + badKeyConversions.add(key) + } + + CORDA_PROPERTY_PREFIX + key + } + }.filterKeys { it.startsWith(CORDA_PROPERTY_PREFIX) } + .mapKeys { it.key.substring(CORDA_PROPERTY_PREFIX.length) } + .filterKeys { !badKeyConversions.contains(it) }) } } diff --git a/node/src/main/kotlin/net/corda/node/services/config/ShadowingException.kt b/node/src/main/kotlin/net/corda/node/services/config/ShadowingException.kt new file mode 100644 index 0000000000..4778fe4284 --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/services/config/ShadowingException.kt @@ -0,0 +1,7 @@ +package net.corda.node.services.config + +import com.typesafe.config.ConfigException + +class ShadowingException(definedProperty : String, convertedProperty : String) + : ConfigException( + "Environment variable $definedProperty is shadowing another property transformed to $convertedProperty") \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index e01a9e0733..8c16c40a34 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -201,7 +201,8 @@ fun driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr inMemoryDB = defaultParameters.inMemoryDB, cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes), djvmBootstrapSource = defaultParameters.djvmBootstrapSource, - djvmCordaSource = defaultParameters.djvmCordaSource + djvmCordaSource = defaultParameters.djvmCordaSource, + environmentVariables = defaultParameters.environmentVariables ), coerce = { it }, dsl = dsl @@ -261,7 +262,8 @@ data class DriverParameters( val inMemoryDB: Boolean = true, val cordappsForAllNodes: Collection? = null, val djvmBootstrapSource: Path? = null, - val djvmCordaSource: List = emptyList() + val djvmCordaSource: List = emptyList(), + val environmentVariables : Map = emptyMap() ) { constructor(cordappsForAllNodes: Collection) : this(isDebug = false, cordappsForAllNodes = cordappsForAllNodes) @@ -301,7 +303,8 @@ data class DriverParameters( // These fields have been added in v4.4 djvmBootstrapSource = null, - djvmCordaSource = emptyList() + djvmCordaSource = emptyList(), + environmentVariables = emptyMap() ) constructor( @@ -420,6 +423,7 @@ data class DriverParameters( fun withCordappsForAllNodes(cordappsForAllNodes: Collection?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes) fun withDjvmBootstrapSource(djvmBootstrapSource: Path?): DriverParameters = copy(djvmBootstrapSource = djvmBootstrapSource) fun withDjvmCordaSource(djvmCordaSource: List): DriverParameters = copy(djvmCordaSource = djvmCordaSource) + fun withEnvironmentVariables(variables : Map): DriverParameters = copy(environmentVariables = variables) fun copy( isDebug: Boolean, @@ -515,9 +519,9 @@ data class DriverParameters( notaryCustomOverrides = notaryCustomOverrides, inMemoryDB = inMemoryDB, cordappsForAllNodes = cordappsForAllNodes, - // These fields have been added in v4.4 djvmBootstrapSource = djvmBootstrapSource, - djvmCordaSource = djvmCordaSource + djvmCordaSource = djvmCordaSource, + environmentVariables = environmentVariables ) } \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index d600410d2c..b22e7bc7e4 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -122,7 +122,8 @@ class DriverDSLImpl( val inMemoryDB: Boolean, val cordappsForAllNodes: Collection?, val djvmBootstrapSource: Path?, - val djvmCordaSource: List + val djvmCordaSource: List, + val environmentVariables : Map ) : InternalDriverDSL { private var _executorService: ScheduledExecutorService? = null @@ -335,9 +336,11 @@ class DriverDSLImpl( } else { startOutOfProcessMiniNode( config, - "initial-registration", - "--network-root-truststore=${rootTruststorePath.toAbsolutePath()}", - "--network-root-truststore-password=$rootTruststorePassword" + arrayOf( + "initial-registration", + "--network-root-truststore=${rootTruststorePath.toAbsolutePath()}", + "--network-root-truststore-password=$rootTruststorePassword" + ) ).map { config } } } @@ -504,7 +507,7 @@ class DriverDSLImpl( } else { // TODO The config we use here is uses a hardocded p2p port which changes when the node is run proper // This causes two node info files to be generated. - startOutOfProcessMiniNode(config, "generate-node-info").map { + startOutOfProcessMiniNode(config, arrayOf("generate-node-info")).map { // Once done we have to read the signed node info file that's been generated val nodeInfoFile = config.corda.baseDirectory.list { paths -> paths.filter { it.fileName.toString().startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get() @@ -590,20 +593,20 @@ class DriverDSLImpl( * Start the node with the given flag which is expected to start the node for some function, which once complete will * terminate the node. */ - @Suppress("SpreadOperator") - private fun startOutOfProcessMiniNode(config: NodeConfig, vararg extraCmdLineFlag: String): CordaFuture { + private fun startOutOfProcessMiniNode(config: NodeConfig, extraCmdLineFlag: Array = emptyArray()): CordaFuture { val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val process = startOutOfProcessNode( - config, - quasarJarPath, - debugPort, - bytemanJarPath, - null, - systemProperties, - "512m", - null, - ZonedDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss.SSS")), - *extraCmdLineFlag + config, + quasarJarPath, + debugPort, + bytemanJarPath, + null, + systemProperties, + "512m", + null, + ZonedDateTime.now().format(DateTimeFormatter.ofPattern("HHmmss.SSS")), + environmentVariables, + extraCmdLineFlag ) return poll(executorService, "$extraCmdLineFlag (${config.corda.myLegalName})") { @@ -664,15 +667,16 @@ class DriverDSLImpl( } else { val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val process = startOutOfProcessNode( - config, - quasarJarPath, - debugPort, - bytemanJarPath, - bytemanPort, - systemProperties, - parameters.maximumHeapSize, - parameters.logLevelOverride, - identifier + config, + quasarJarPath, + debugPort, + bytemanJarPath, + bytemanPort, + systemProperties, + parameters.maximumHeapSize, + parameters.logLevelOverride, + identifier, + environmentVariables ) // Destroy the child process when the parent exits.This is needed even when `waitForAllNodesToFinish` is @@ -838,16 +842,17 @@ class DriverDSLImpl( @Suppress("ComplexMethod", "MaxLineLength", "LongParameterList") private fun startOutOfProcessNode( - config: NodeConfig, - quasarJarPath: String, - debugPort: Int?, - bytemanJarPath: String?, - bytemanPort: Int?, - overriddenSystemProperties: Map, - maximumHeapSize: String, - logLevelOverride: String?, - identifier: String, - vararg extraCmdLineFlag: String + config: NodeConfig, + quasarJarPath: String, + debugPort: Int?, + bytemanJarPath: String?, + bytemanPort: Int?, + overriddenSystemProperties: Map, + maximumHeapSize: String, + logLevelOverride: String?, + identifier: String, + environmentVariables : Map, + extraCmdLineFlag: Array = emptyArray() ): Process { log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " + "debug port is " + (debugPort ?: "not enabled") + ", " + @@ -892,7 +897,7 @@ class DriverDSLImpl( "--base-directory=${config.corda.baseDirectory}", "--logging-level=$loggingLevel", "--no-local-shell").also { - it += extraCmdLineFlag + it.addAll(extraCmdLineFlag) }.toList() val bytemanJvmArgs = { @@ -920,14 +925,15 @@ class DriverDSLImpl( } return ProcessUtilities.startJavaProcess( - className = "net.corda.node.Corda", // cannot directly get class for this, so just use string - arguments = arguments, - jdwpPort = debugPort, - extraJvmArguments = extraJvmArguments + bytemanJvmArgs + "-Dnet.corda.node.printErrorsToStdErr=true", - workingDirectory = config.corda.baseDirectory, - maximumHeapSize = maximumHeapSize, - classPath = cp, - identifier = identifier + className = "net.corda.node.Corda", // cannot directly get class for this, so just use string + arguments = arguments, + jdwpPort = debugPort, + extraJvmArguments = extraJvmArguments + bytemanJvmArgs + "-Dnet.corda.node.printErrorsToStdErr=true", + workingDirectory = config.corda.baseDirectory, + maximumHeapSize = maximumHeapSize, + classPath = cp, + identifier = identifier, + environmentVariables = environmentVariables ) } @@ -1138,24 +1144,25 @@ fun genericDriver( val serializationEnv = setDriverSerialization() val driverDsl = driverDslWrapper( DriverDSLImpl( - portAllocation = defaultParameters.portAllocation, - debugPortAllocation = defaultParameters.debugPortAllocation, - systemProperties = defaultParameters.systemProperties, - driverDirectory = defaultParameters.driverDirectory.toAbsolutePath(), - useTestClock = defaultParameters.useTestClock, - isDebug = defaultParameters.isDebug, - startNodesInProcess = defaultParameters.startNodesInProcess, - waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish, - extraCordappPackagesToScan = @Suppress("DEPRECATION") defaultParameters.extraCordappPackagesToScan, - jmxPolicy = defaultParameters.jmxPolicy, - notarySpecs = defaultParameters.notarySpecs, - compatibilityZone = null, - networkParameters = defaultParameters.networkParameters, - notaryCustomOverrides = defaultParameters.notaryCustomOverrides, - inMemoryDB = defaultParameters.inMemoryDB, - cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes), - djvmBootstrapSource = defaultParameters.djvmBootstrapSource, - djvmCordaSource = defaultParameters.djvmCordaSource + portAllocation = defaultParameters.portAllocation, + debugPortAllocation = defaultParameters.debugPortAllocation, + systemProperties = defaultParameters.systemProperties, + driverDirectory = defaultParameters.driverDirectory.toAbsolutePath(), + useTestClock = defaultParameters.useTestClock, + isDebug = defaultParameters.isDebug, + startNodesInProcess = defaultParameters.startNodesInProcess, + waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish, + extraCordappPackagesToScan = @Suppress("DEPRECATION") defaultParameters.extraCordappPackagesToScan, + jmxPolicy = defaultParameters.jmxPolicy, + notarySpecs = defaultParameters.notarySpecs, + compatibilityZone = null, + networkParameters = defaultParameters.networkParameters, + notaryCustomOverrides = defaultParameters.notaryCustomOverrides, + inMemoryDB = defaultParameters.inMemoryDB, + cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes), + djvmBootstrapSource = defaultParameters.djvmBootstrapSource, + djvmCordaSource = defaultParameters.djvmCordaSource, + environmentVariables = defaultParameters.environmentVariables ) ) val shutdownHook = addShutdownHook(driverDsl::shutdown) @@ -1252,28 +1259,30 @@ fun internalDriver( cordappsForAllNodes: Collection? = null, djvmBootstrapSource: Path? = null, djvmCordaSource: List = emptyList(), + environmentVariables: Map = emptyMap(), dsl: DriverDSLImpl.() -> A ): A { return genericDriver( driverDsl = DriverDSLImpl( - portAllocation = portAllocation, - debugPortAllocation = debugPortAllocation, - systemProperties = systemProperties, - driverDirectory = driverDirectory.toAbsolutePath(), - useTestClock = useTestClock, - isDebug = isDebug, - startNodesInProcess = startNodesInProcess, - waitForAllNodesToFinish = waitForAllNodesToFinish, - extraCordappPackagesToScan = extraCordappPackagesToScan, - notarySpecs = notarySpecs, - jmxPolicy = jmxPolicy, - compatibilityZone = compatibilityZone, - networkParameters = networkParameters, - notaryCustomOverrides = notaryCustomOverrides, - inMemoryDB = inMemoryDB, - cordappsForAllNodes = cordappsForAllNodes, - djvmBootstrapSource = djvmBootstrapSource, - djvmCordaSource = djvmCordaSource + portAllocation = portAllocation, + debugPortAllocation = debugPortAllocation, + systemProperties = systemProperties, + driverDirectory = driverDirectory.toAbsolutePath(), + useTestClock = useTestClock, + isDebug = isDebug, + startNodesInProcess = startNodesInProcess, + waitForAllNodesToFinish = waitForAllNodesToFinish, + extraCordappPackagesToScan = extraCordappPackagesToScan, + notarySpecs = notarySpecs, + jmxPolicy = jmxPolicy, + compatibilityZone = compatibilityZone, + networkParameters = networkParameters, + notaryCustomOverrides = notaryCustomOverrides, + inMemoryDB = inMemoryDB, + cordappsForAllNodes = cordappsForAllNodes, + djvmBootstrapSource = djvmBootstrapSource, + djvmCordaSource = djvmCordaSource, + environmentVariables = environmentVariables ), coerce = { it }, dsl = dsl diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt index a6ba45a5b2..9acff0d020 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/ProcessUtilities.kt @@ -5,15 +5,26 @@ import java.io.File import java.nio.file.Path object ProcessUtilities { + @Suppress("LongParameterList") inline fun startJavaProcess( arguments: List, classPath: List = defaultClassPath, workingDirectory: Path? = null, jdwpPort: Int? = null, extraJvmArguments: List = emptyList(), - maximumHeapSize: String? = null + maximumHeapSize: String? = null, + environmentVariables: Map = emptyMap() ): Process { - return startJavaProcess(C::class.java.name, arguments, classPath, workingDirectory, jdwpPort, extraJvmArguments, maximumHeapSize) + return startJavaProcess( + C::class.java.name, + arguments, + classPath, + workingDirectory, + jdwpPort, + extraJvmArguments, + maximumHeapSize, + environmentVariables = environmentVariables + ) } @Suppress("LongParameterList") @@ -25,7 +36,8 @@ object ProcessUtilities { jdwpPort: Int? = null, extraJvmArguments: List = emptyList(), maximumHeapSize: String? = null, - identifier: String = "" + identifier: String = "", + environmentVariables: Map = emptyMap() ): Process { val command = mutableListOf().apply { add(javaPath) @@ -38,6 +50,7 @@ object ProcessUtilities { } return ProcessBuilder(command).apply { inheritIO() + environment().putAll(environmentVariables) environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator) if (workingDirectory != null) { // An identifier may be handy if the same process started, killed and then re-started. Without the identifier diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt index 53da93b860..d6eb12b0b1 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt @@ -124,6 +124,7 @@ fun rpcDriver( cordappsForAllNodes: Collection? = null, djvmBootstrapSource: Path? = null, djvmCordaSource: List = emptyList(), + environmentVariables: Map = emptyMap(), dsl: RPCDriverDSL.() -> A ): A { return genericDriver( @@ -146,7 +147,8 @@ fun rpcDriver( inMemoryDB = inMemoryDB, cordappsForAllNodes = cordappsForAllNodes, djvmBootstrapSource = djvmBootstrapSource, - djvmCordaSource = djvmCordaSource + djvmCordaSource = djvmCordaSource, + environmentVariables = environmentVariables ), externalTrace ), coerce = { it },