Move all the configuration for running doorman in its configuration f… (#104)

* Move all the configuration for running doorman in its configuration files
This commit is contained in:
Alberto Arri 2017-11-15 13:04:16 +00:00 committed by GitHub
parent fffcdb47da
commit 233f1fb8e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 127 additions and 54 deletions

6
.idea/compiler.xml generated
View File

@ -46,6 +46,8 @@
<module name="finance_integrationTest" target="1.8" /> <module name="finance_integrationTest" target="1.8" />
<module name="finance_main" target="1.8" /> <module name="finance_main" target="1.8" />
<module name="finance_test" target="1.8" /> <module name="finance_test" target="1.8" />
<module name="gradle-plugins-cordform-common_main" target="1.8" />
<module name="gradle-plugins-cordform-common_test" target="1.8" />
<module name="graphs_main" target="1.8" /> <module name="graphs_main" target="1.8" />
<module name="graphs_test" target="1.8" /> <module name="graphs_test" target="1.8" />
<module name="intellij-plugin_main" target="1.8" /> <module name="intellij-plugin_main" target="1.8" />
@ -88,8 +90,12 @@
<module name="notary-demo_test" target="1.8" /> <module name="notary-demo_test" target="1.8" />
<module name="perftestcordapp_main" target="1.8" /> <module name="perftestcordapp_main" target="1.8" />
<module name="perftestcordapp_test" target="1.8" /> <module name="perftestcordapp_test" target="1.8" />
<module name="publish-utils_main" target="1.8" />
<module name="publish-utils_test" target="1.8" />
<module name="quasar-hook_main" target="1.8" /> <module name="quasar-hook_main" target="1.8" />
<module name="quasar-hook_test" target="1.8" /> <module name="quasar-hook_test" target="1.8" />
<module name="quasar-utils_main" target="1.8" />
<module name="quasar-utils_test" target="1.8" />
<module name="rpc_integrationTest" target="1.8" /> <module name="rpc_integrationTest" target="1.8" />
<module name="rpc_main" target="1.8" /> <module name="rpc_main" target="1.8" />
<module name="rpc_smokeTest" target="1.8" /> <module name="rpc_smokeTest" target="1.8" />

View File

@ -15,6 +15,50 @@ This is an example of what a Doorman configuration file might look like:
Invoke Doorman with ``-?`` for a full list of supported command-line arguments. Invoke Doorman with ``-?`` for a full list of supported command-line arguments.
Configuration parameters
------------------------
Allowed parameters are:
:keystorePassword: the keystore password
:caPrivateKeyPassword: the ca private key password
:rootKeystorePassword: Password for the root store
:rootPrivateKeyPassword: Password for the root private key
:host: host on which doorman runs
:port: port on which doorman runs
:mode: must be one of: DOORMAN (default), CA_KEYGEN, ROOT_KEYGEN.
:approveAll: Whether to approve all request (defaults to false), this is for debug only.
:databaseProperties: database properties
:dataSourceProperties: datasoruce properties
:jiraConfig: The Jira configuration
:address: The URL to use to connect to Jira
:projectCode: The project code on Jira
:username: Username for Jira
:password: Password for Jira
:doneTransitionCode: Jira status to put approved tickets in
:keystorePath: Path for the keystore
:rootStorePath: Path for the root keystore
:approveInterval: How often to process Jira approved requests in seconds
:signInterval: How often to sign the network map in seconds
Bootstrapping the network parameters Bootstrapping the network parameters
------------------------------------ ------------------------------------
When Doorman is running it will serve the current network parameters. The first time Doorman is When Doorman is running it will serve the current network parameters. The first time Doorman is

View File

@ -1,3 +1,4 @@
basedir = "."
host = localhost host = localhost
port = 0 port = 0
keystorePath = ${basedir}"/certificates/caKeystore.jks" keystorePath = ${basedir}"/certificates/caKeystore.jks"

View File

@ -1,10 +1,14 @@
package com.r3.corda.networkmanage.doorman package com.r3.corda.networkmanage.doorman
import com.r3.corda.networkmanage.common.utils.ShowHelpException
import com.r3.corda.networkmanage.common.utils.toConfigWithOptions import com.r3.corda.networkmanage.common.utils.toConfigWithOptions
import com.r3.corda.networkmanage.doorman.DoormanParameters.Companion.DEFAULT_APPROVE_INTERVAL import com.r3.corda.networkmanage.doorman.DoormanParameters.Companion.DEFAULT_APPROVE_INTERVAL
import com.r3.corda.networkmanage.doorman.DoormanParameters.Companion.DEFAULT_SIGN_INTERVAL import com.r3.corda.networkmanage.doorman.DoormanParameters.Companion.DEFAULT_SIGN_INTERVAL
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigParseOptions
import joptsimple.OptionParser
import net.corda.core.internal.isRegularFile
import net.corda.core.internal.exists
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.config.parseAs
@ -13,24 +17,22 @@ import java.nio.file.Paths
import java.time.Duration import java.time.Duration
import java.util.* import java.util.*
data class DoormanParameters(val basedir: Path, data class DoormanParameters(val keystorePassword: String?,
val keystorePassword: String?,
val caPrivateKeyPassword: String?, val caPrivateKeyPassword: String?,
val rootKeystorePassword: String?, val rootKeystorePassword: String?,
val rootPrivateKeyPassword: String?, val rootPrivateKeyPassword: String?,
val host: String, val host: String,
val port: Int, val port: Int,
val dataSourceProperties: Properties, val dataSourceProperties: Properties,
val mode: Mode, val mode: Mode = Mode.DOORMAN,
val approveAll: Boolean = false, val approveAll: Boolean = false,
val databaseProperties: Properties? = null, val databaseProperties: Properties? = null,
val jiraConfig: JiraConfig? = null, val jiraConfig: JiraConfig? = null,
val keystorePath: Path? = null, // basedir / "certificates" / "caKeystore.jks", val keystorePath: Path? = null,
val rootStorePath: Path? = null, // basedir / "certificates" / "rootCAKeystore.jks" val rootStorePath: Path? = null,
// TODO Change these to Duration in the future // TODO Change these to Duration in the future
val approveInterval: Long = DEFAULT_APPROVE_INTERVAL, val approveInterval: Long = DEFAULT_APPROVE_INTERVAL,
val signInterval: Long = DEFAULT_SIGN_INTERVAL, val signInterval: Long = DEFAULT_SIGN_INTERVAL
val initialNetworkParameters: Path
) { ) {
enum class Mode { enum class Mode {
DOORMAN, CA_KEYGEN, ROOT_KEYGEN DOORMAN, CA_KEYGEN, ROOT_KEYGEN
@ -50,30 +52,48 @@ data class DoormanParameters(val basedir: Path,
} }
} }
fun parseParameters(vararg args: String): DoormanParameters { data class CommandLineOptions(val configFile: Path,
val argConfig = args.toConfigWithOptions { val initialNetworkParameters: Path) {
accepts("basedir", "Overriding configuration filepath, default to current directory.").withRequiredArg().defaultsTo(".").describedAs("filepath") init {
accepts("configFile", "Overriding configuration file, default to <<current directory>>/node.conf.").withRequiredArg().describedAs("filepath") check(configFile.isRegularFile()) { "Config file $configFile does not exist" }
accepts("mode", "Execution mode. Allowed values: ${DoormanParameters.Mode.values().toList()}").withRequiredArg().defaultsTo(DoormanParameters.Mode.DOORMAN.name) check(initialNetworkParameters.isRegularFile()) { "Initial network parameters file $initialNetworkParameters does not exist" }
accepts("keystorePath", "CA keystore filepath").withRequiredArg().describedAs("filepath")
accepts("rootStorePath", "Root CA keystore filepath").withRequiredArg().describedAs("filepath")
accepts("keystorePassword", "CA keystore password.").withRequiredArg().describedAs("password")
accepts("caPrivateKeyPassword", "CA private key password.").withRequiredArg().describedAs("password")
accepts("rootKeystorePassword", "Root CA keystore password.").withRequiredArg().describedAs("password")
accepts("rootPrivateKeyPassword", "Root private key password.").withRequiredArg().describedAs("password")
accepts("host", "Doorman web service host override").withRequiredArg().describedAs("hostname")
accepts("port", "Doorman web service port override").withRequiredArg().ofType(Int::class.java).describedAs("port number")
accepts("approveInterval", "Time interval (in seconds) in which CSRs are approved (default: ${DEFAULT_APPROVE_INTERVAL})").withRequiredArg().ofType(Long::class.java).defaultsTo(DEFAULT_APPROVE_INTERVAL)
accepts("signInterval", "Time interval (in seconds) in which network map is signed (default: ${DEFAULT_SIGN_INTERVAL})").withRequiredArg().ofType(Long::class.java).defaultsTo(DEFAULT_SIGN_INTERVAL)
accepts("initialNetworkParameters", "initial network parameters filepath").withRequiredArg().describedAs("The initial network map").describedAs("filepath")
} }
}
val configFile = if (argConfig.hasPath("configFile")) { /**
Paths.get(argConfig.getString("configFile")) * Parses the doorman command line options.
} else { */
Paths.get(argConfig.getString("basedir")) / "node.conf" fun parseCommandLine(vararg args: String): CommandLineOptions {
val optionParser = OptionParser()
val configFileArg = optionParser
.accepts("config-file", "The path to the config file")
.withRequiredArg()
.describedAs("filepath")
val initialNetworkParametersArg = optionParser
.accepts("initial-network-parameters", "initial network parameters filepath")
.withRequiredArg()
.describedAs("The initial network map")
.describedAs("filepath")
val helpOption = optionParser.acceptsAll(listOf("h", "?", "help"), "show help").forHelp();
val optionSet = optionParser.parse(*args)
// Print help and exit on help option.
if (optionSet.has(helpOption)) {
throw ShowHelpException(optionParser)
} }
return argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))) val configFile = Paths.get(optionSet.valueOf(configFileArg)).toAbsolutePath()
val initialNetworkParameters = Paths.get(optionSet.valueOf(initialNetworkParametersArg)).toAbsolutePath()
return CommandLineOptions(configFile, initialNetworkParameters)
}
/**
* Parses a configuration file, which contains all the configuration except the initial values for the network
* parameters.
*/
fun parseParameters(configFile: Path): DoormanParameters {
return ConfigFactory
.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))
.resolve() .resolve()
.parseAs() .parseAs()
} }

View File

@ -245,8 +245,8 @@ private class ApproveAllCertificateRequestStorage(private val delegate: Certific
fun main(args: Array<String>) { fun main(args: Array<String>) {
try { try {
// TODO : Remove config overrides and solely use config file after testnet is finalized. val commandLineOptions = parseCommandLine(*args)
parseParameters(*args).run { parseParameters(commandLineOptions.configFile).run {
when (mode) { when (mode) {
DoormanParameters.Mode.ROOT_KEYGEN -> generateRootKeyPair( DoormanParameters.Mode.ROOT_KEYGEN -> generateRootKeyPair(
rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"), rootStorePath ?: throw IllegalArgumentException("The 'rootStorePath' parameter must be specified when generating keys!"),
@ -263,7 +263,7 @@ fun main(args: Array<String>) {
val database = configureDatabase(dataSourceProperties, databaseProperties, { throw UnsupportedOperationException() }, SchemaService()) val database = configureDatabase(dataSourceProperties, databaseProperties, { throw UnsupportedOperationException() }, SchemaService())
val signer = buildLocalSigner(this) val signer = buildLocalSigner(this)
val networkParameters = parseNetworkParametersFrom(initialNetworkParameters) val networkParameters = parseNetworkParametersFrom(commandLineOptions.initialNetworkParameters)
startDoorman(NetworkHostAndPort(host, port), database, approveAll, networkParameters, signer, approveInterval, signInterval, jiraConfig) startDoorman(NetworkHostAndPort(host, port), database, approveAll, networkParameters, signer, approveInterval, signInterval, jiraConfig)
} }
} }

View File

@ -1,51 +1,52 @@
package com.r3.corda.networkmanage.doorman package com.r3.corda.networkmanage.doorman
import com.r3.corda.networkmanage.TestBase import com.r3.corda.networkmanage.common.utils.ShowHelpException
import com.typesafe.config.ConfigException import com.typesafe.config.ConfigException
import net.corda.core.utilities.seconds import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
import java.nio.file.Paths
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
class DoormanParametersTest : TestBase() { class DoormanParametersTest {
private val testDummyPath = ".${File.separator}testDummyPath.jks"
private val validInitialNetworkConfigPath = File(javaClass.getResource("/initial-network-parameters.conf").toURI()).absolutePath private val validInitialNetworkConfigPath = File(javaClass.getResource("/initial-network-parameters.conf").toURI()).absolutePath
private val validConfigPath = File(javaClass.getResource("/doorman.conf").toURI()).absolutePath private val validConfigPath = File(javaClass.getResource("/doorman.conf").toURI()).absolutePath
private val invalidConfigPath = File(javaClass.getResource("/doorman_fail.conf").toURI()).absolutePath private val invalidConfigPath = File(javaClass.getResource("/doorman_fail.conf").toURI()).absolutePath
private val validArgs = arrayOf("--config-file", validConfigPath, "--initial-network-parameters", validInitialNetworkConfigPath)
private val requiredArgs = arrayOf("--configFile", validConfigPath, "--initialNetworkParameters", validInitialNetworkConfigPath)
@Test @Test
fun `parse mode flag arg correctly`() { fun `should fail when initial network parameters file is missing`() {
assertEquals(DoormanParameters.Mode.CA_KEYGEN, callParseParametersWithRequiredArgs("--mode", "CA_KEYGEN").mode) val message = assertFailsWith<IllegalStateException> {
assertEquals(DoormanParameters.Mode.ROOT_KEYGEN, callParseParametersWithRequiredArgs("--mode", "ROOT_KEYGEN").mode) parseCommandLine("--config-file", validConfigPath, "--initial-network-parameters", "not-here")
assertEquals(DoormanParameters.Mode.DOORMAN, callParseParametersWithRequiredArgs("--mode", "DOORMAN").mode) }.message
assertThat(message).contains("Initial network parameters file ")
} }
@Test @Test
fun `command line arg should override config file`() { fun `should fail when config file is missing`() {
val params = callParseParametersWithRequiredArgs("--keystorePath", testDummyPath, "--port", "1000") val message = assertFailsWith<IllegalStateException> {
assertEquals(testDummyPath, params.keystorePath.toString()) parseCommandLine("--config-file", "not-existing-file", "--initial-network-parameters", validInitialNetworkConfigPath)
assertEquals(1000, params.port) }.message
assertThat(message).contains("Config file ")
}
val params2 = callParseParametersWithRequiredArgs() @Test
assertEquals(Paths.get("/opt/doorman/certificates/caKeystore.jks"), params2.keystorePath) fun `should throw ShowHelpException when -? is on the command line`() {
assertEquals(8080, params2.port) assertFailsWith<ShowHelpException> {
parseCommandLine("-?")
}
} }
@Test @Test
fun `should fail when config missing`() { fun `should fail when config missing`() {
// dataSourceProperties is missing from node_fail.conf and it should fail during parsing, and shouldn't use default from reference.conf.
assertFailsWith<ConfigException.Missing> { assertFailsWith<ConfigException.Missing> {
parseParameters("--configFile", invalidConfigPath) parseParameters(parseCommandLine("--config-file", invalidConfigPath, "--initial-network-parameters", validInitialNetworkConfigPath).configFile)
} }
} }
@Test @Test
fun `should parse jira config correctly`() { fun `should parse jira config correctly`() {
val parameter = callParseParametersWithRequiredArgs() val parameter = parseCommandLineAndGetParameters()
assertEquals("https://doorman-jira-host.com/", parameter.jiraConfig?.address) assertEquals("https://doorman-jira-host.com/", parameter.jiraConfig?.address)
assertEquals("TD", parameter.jiraConfig?.projectCode) assertEquals("TD", parameter.jiraConfig?.projectCode)
assertEquals("username", parameter.jiraConfig?.username) assertEquals("username", parameter.jiraConfig?.username)
@ -53,7 +54,7 @@ class DoormanParametersTest : TestBase() {
assertEquals(41, parameter.jiraConfig?.doneTransitionCode) assertEquals(41, parameter.jiraConfig?.doneTransitionCode)
} }
private fun callParseParametersWithRequiredArgs(vararg additionalArgs: String): DoormanParameters { private fun parseCommandLineAndGetParameters(): DoormanParameters {
return parseParameters(*(requiredArgs + additionalArgs)) return parseParameters(parseCommandLine(*validArgs).configFile)
} }
} }

View File

@ -1,3 +1,4 @@
basedir="."
keystorePath = "/opt/doorman/certificates/caKeystore.jks" keystorePath = "/opt/doorman/certificates/caKeystore.jks"
keyStorePassword = "password" keyStorePassword = "password"
caPrivateKeyPassword = "password" caPrivateKeyPassword = "password"