diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt deleted file mode 100644 index fd2f7d7507..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt +++ /dev/null @@ -1,110 +0,0 @@ -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(timeout=300_000) - 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(timeout=300_000) - 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(timeout=300_000) - 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(timeout=300_000) - 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(timeout=300_000) - 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/services/config/NodeConfigParsingTests.kt b/node/src/integration-test/kotlin/net/corda/node/services/config/NodeConfigParsingTests.kt new file mode 100644 index 0000000000..2f704bf630 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/services/config/NodeConfigParsingTests.kt @@ -0,0 +1,32 @@ +package net.corda.node.services.config + +import net.corda.core.utilities.getOrThrow +import net.corda.node.logging.logFile +import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.driver +import net.corda.testing.driver.internal.incrementalPortAllocation +import org.junit.Assert.assertTrue +import org.junit.Test + +class NodeConfigParsingTests { + @Test(timeout = 300_000) + 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/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt index 6aaac65251..d4e0239537 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 @@ -6,6 +6,7 @@ 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.VisibleForTesting import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.exists @@ -36,11 +37,34 @@ object ConfigHelper { private const val UPPERCASE_PROPERTY_PREFIX = "CORDA." private val log = LoggerFactory.getLogger(javaClass) + + val DEFAULT_CONFIG_FILENAME = "node.conf" + @Suppress("LongParameterList") fun loadConfig(baseDirectory: Path, - configFile: Path = baseDirectory / "node.conf", + configFile: Path = baseDirectory / DEFAULT_CONFIG_FILENAME, allowMissingConfig: Boolean = false, - configOverrides: Config = ConfigFactory.empty()): Config { + configOverrides: Config = ConfigFactory.empty()): Config + = loadConfig(baseDirectory, + configFile = configFile, + allowMissingConfig = allowMissingConfig, + configOverrides = configOverrides, + rawSystemOverrides = ConfigFactory.systemProperties(), + rawEnvironmentOverrides = ConfigFactory.systemEnvironment()) + + /** + * Internal equivalent of [loadConfig] which allows the system and environment + * overrides to be provided from a test. + */ + @Suppress("LongParameterList") + @VisibleForTesting + internal fun loadConfig(baseDirectory: Path, + configFile: Path, + allowMissingConfig: Boolean, + configOverrides: Config, + rawSystemOverrides: Config, + rawEnvironmentOverrides: Config + ): Config { val parseOptions = ConfigParseOptions.defaults() val defaultConfig = ConfigFactory.parseResources("corda-reference.conf", parseOptions.setAllowMissing(false)) val appConfig = ConfigFactory.parseFile(configFile.toFile(), parseOptions.setAllowMissing(allowMissingConfig)) @@ -55,8 +79,8 @@ object ConfigHelper { "flowExternalOperationThreadPoolSize" to min(coreCount, FLOW_EXTERNAL_OPERATION_THREAD_POOL_SIZE_MAX).toString() ) - val systemOverrides = ConfigFactory.systemProperties().cordaEntriesOnly() - val environmentOverrides = ConfigFactory.systemEnvironment().cordaEntriesOnly() + val systemOverrides = rawSystemOverrides.cordaEntriesOnly() + val environmentOverrides = rawEnvironmentOverrides.cordaEntriesOnly() val finalConfig = configOf( // Add substitution values here "baseDirectory" to baseDirectory.toString()) @@ -91,8 +115,8 @@ object ConfigHelper { .mapKeys { val original = it.key as String - // Reject environment variable that are in all caps - // since these cannot be properties. + // Reject environment variable that are in all caps + // since these cannot be properties. if (original == original.toUpperCase()){ return@mapKeys original } diff --git a/node/src/test/kotlin/net/corda/node/internal/NodeStartupTest.kt b/node/src/test/kotlin/net/corda/node/internal/NodeStartupTest.kt index ccaa925268..da793ee97c 100644 --- a/node/src/test/kotlin/net/corda/node/internal/NodeStartupTest.kt +++ b/node/src/test/kotlin/net/corda/node/internal/NodeStartupTest.kt @@ -1,9 +1,9 @@ package net.corda.node.internal -import com.google.common.io.Files import org.assertj.core.api.Assertions.assertThat import org.junit.Test import java.nio.channels.OverlappingFileLockException +import java.nio.file.Files import java.util.concurrent.CountDownLatch import kotlin.concurrent.thread import kotlin.test.assertFailsWith @@ -11,8 +11,7 @@ import kotlin.test.assertFailsWith class NodeStartupTest { @Test(timeout=300_000) fun `test that you cant start two nodes in the same directory`() { - val dir = Files.createTempDir().toPath() - + val dir = Files.createTempDirectory("node_startup_test") val latch = CountDownLatch(1) thread(start = true) { diff --git a/node/src/test/kotlin/net/corda/node/services/config/ConfigHelperTests.kt b/node/src/test/kotlin/net/corda/node/services/config/ConfigHelperTests.kt new file mode 100644 index 0000000000..cd378de825 --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/services/config/ConfigHelperTests.kt @@ -0,0 +1,83 @@ +package net.corda.node.services.config + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import net.corda.core.internal.delete +import net.corda.core.internal.div +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import java.nio.file.Files +import java.nio.file.Path + +class ConfigHelperTests { + private var baseDir: Path? = null + + @Before + fun setup() { + baseDir = Files.createTempDirectory("corda_config") + } + + @After + fun cleanup() { + baseDir?.delete() + } + + @Test(timeout = 300_000) + fun `config is overridden by underscore variable`() { + val sshPort: Long = 9000 + + // Verify the port isn't set when not provided + var config = loadConfig() + Assert.assertFalse("SSH port should not be configured when not provided", config!!.hasPath("sshd.port")) + + config = loadConfig("corda_sshd_port" to sshPort) + Assert.assertEquals(sshPort, config?.getLong("sshd.port")) + } + + @Test(timeout = 300_000) + fun `config is overridden by case insensitive underscore variable`() { + val sshPort: Long = 10000 + val config = loadConfig("CORDA_sshd_port" to sshPort) + Assert.assertEquals(sshPort, config?.getLong("sshd.port")) + } + + @Test(timeout = 300_000) + fun `config is overridden by case insensitive dot variable`() { + val sshPort: Long = 11000 + val config = loadConfig("CORDA.sshd.port" to sshPort, + "corda.devMode" to true.toString()) + Assert.assertEquals(sshPort, config?.getLong("sshd.port")) + } + + @Test(timeout = 300_000, expected = ShadowingException::class) + fun `shadowing is forbidden`() { + val sshPort: Long = 12000 + loadConfig("CORDA_sshd_port" to sshPort.toString(), + "corda.sshd.port" to sshPort.toString()) + } + + /** + * Load the node configuration with the given environment variable + * overrides. + * + * @param environmentVariables pairs of keys and values for environment variables + * to simulate when loading the configuration. + */ + @Suppress("SpreadOperator") + private fun loadConfig(vararg environmentVariables: Pair): Config? { + return baseDir?.let { + ConfigHelper.loadConfig( + baseDirectory = it, + configFile = it / ConfigHelper.DEFAULT_CONFIG_FILENAME, + allowMissingConfig = true, + configOverrides = ConfigFactory.empty(), + rawSystemOverrides = ConfigFactory.empty(), + rawEnvironmentOverrides = ConfigFactory.empty().plus( + mapOf(*environmentVariables) + ) + ) + } + } +}