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_main" 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_test" 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="perftestcordapp_main" 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_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_main" 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.
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
------------------------------------
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
port = 0
keystorePath = ${basedir}"/certificates/caKeystore.jks"

View File

@ -1,10 +1,14 @@
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.doorman.DoormanParameters.Companion.DEFAULT_APPROVE_INTERVAL
import com.r3.corda.networkmanage.doorman.DoormanParameters.Companion.DEFAULT_SIGN_INTERVAL
import com.typesafe.config.ConfigFactory
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.utilities.seconds
import net.corda.nodeapi.config.parseAs
@ -13,24 +17,22 @@ import java.nio.file.Paths
import java.time.Duration
import java.util.*
data class DoormanParameters(val basedir: Path,
val keystorePassword: String?,
data class DoormanParameters(val keystorePassword: String?,
val caPrivateKeyPassword: String?,
val rootKeystorePassword: String?,
val rootPrivateKeyPassword: String?,
val host: String,
val port: Int,
val dataSourceProperties: Properties,
val mode: Mode,
val mode: Mode = Mode.DOORMAN,
val approveAll: Boolean = false,
val databaseProperties: Properties? = null,
val jiraConfig: JiraConfig? = null,
val keystorePath: Path? = null, // basedir / "certificates" / "caKeystore.jks",
val rootStorePath: Path? = null, // basedir / "certificates" / "rootCAKeystore.jks"
val keystorePath: Path? = null,
val rootStorePath: Path? = null,
// TODO Change these to Duration in the future
val approveInterval: Long = DEFAULT_APPROVE_INTERVAL,
val signInterval: Long = DEFAULT_SIGN_INTERVAL,
val initialNetworkParameters: Path
val signInterval: Long = DEFAULT_SIGN_INTERVAL
) {
enum class Mode {
DOORMAN, CA_KEYGEN, ROOT_KEYGEN
@ -50,30 +52,48 @@ data class DoormanParameters(val basedir: Path,
}
}
fun parseParameters(vararg args: String): DoormanParameters {
val argConfig = args.toConfigWithOptions {
accepts("basedir", "Overriding configuration filepath, default to current directory.").withRequiredArg().defaultsTo(".").describedAs("filepath")
accepts("configFile", "Overriding configuration file, default to <<current directory>>/node.conf.").withRequiredArg().describedAs("filepath")
accepts("mode", "Execution mode. Allowed values: ${DoormanParameters.Mode.values().toList()}").withRequiredArg().defaultsTo(DoormanParameters.Mode.DOORMAN.name)
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")
data class CommandLineOptions(val configFile: Path,
val initialNetworkParameters: Path) {
init {
check(configFile.isRegularFile()) { "Config file $configFile does not exist" }
check(initialNetworkParameters.isRegularFile()) { "Initial network parameters file $initialNetworkParameters does not exist" }
}
}
val configFile = if (argConfig.hasPath("configFile")) {
Paths.get(argConfig.getString("configFile"))
} else {
Paths.get(argConfig.getString("basedir")) / "node.conf"
/**
* Parses the doorman command line options.
*/
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()
.parseAs()
}

View File

@ -245,8 +245,8 @@ private class ApproveAllCertificateRequestStorage(private val delegate: Certific
fun main(args: Array<String>) {
try {
// TODO : Remove config overrides and solely use config file after testnet is finalized.
parseParameters(*args).run {
val commandLineOptions = parseCommandLine(*args)
parseParameters(commandLineOptions.configFile).run {
when (mode) {
DoormanParameters.Mode.ROOT_KEYGEN -> generateRootKeyPair(
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 signer = buildLocalSigner(this)
val networkParameters = parseNetworkParametersFrom(initialNetworkParameters)
val networkParameters = parseNetworkParametersFrom(commandLineOptions.initialNetworkParameters)
startDoorman(NetworkHostAndPort(host, port), database, approveAll, networkParameters, signer, approveInterval, signInterval, jiraConfig)
}
}

View File

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

View File

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