mirror of
https://github.com/corda/corda.git
synced 2025-03-15 16:46:12 +00:00
ENT-2611: Standardise CLI for corda firewall (#1503)
* Update firewall to use picocli * Update documentation * Remove joptsimple dependency * Fix broken tests * Grammar fix
This commit is contained in:
parent
9edc15d018
commit
abfe538e6e
@ -42,6 +42,10 @@ dependencies {
|
||||
transitive = false// we control dependencies directly as the bridge is likely to be audited
|
||||
}
|
||||
|
||||
compile(project(':tools:cliutils')) {
|
||||
transitive = false// we control dependencies directly as the bridge is likely to be audited
|
||||
}
|
||||
|
||||
// Here we pull in dependencies that would normally be pulled in transitively from :core and :node-api, but we need more fine grained control
|
||||
// For AMQP serialisation.
|
||||
compile "org.apache.qpid:proton-j:${protonj_version}"
|
||||
@ -69,12 +73,15 @@ dependencies {
|
||||
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
|
||||
compile "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
||||
|
||||
// JOpt: for command line flags.
|
||||
compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version"
|
||||
|
||||
// Manifests: for reading stuff from the manifest file
|
||||
compile "com.jcabi:jcabi-manifests:1.1"
|
||||
|
||||
// Picocli: for parsing command line options
|
||||
compile "info.picocli:picocli:$picocli_version"
|
||||
|
||||
// JAnsi: for drawing things to the terminal in nicely coloured ways.
|
||||
compile "org.fusesource.jansi:jansi:$jansi_version"
|
||||
|
||||
integrationTestCompile project(':node-driver')
|
||||
integrationTestCompile "org.apache.curator:curator-test:${curator_version}"
|
||||
testCompile "junit:junit:$junit_version"
|
||||
|
@ -3,8 +3,9 @@
|
||||
package net.corda.bridge
|
||||
|
||||
import net.corda.bridge.internal.FirewallStartup
|
||||
import net.corda.cliutils.start
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
exitProcess(if (FirewallStartup(args).run()) 0 else 1)
|
||||
FirewallStartup().start(args)
|
||||
}
|
||||
|
@ -1,66 +0,0 @@
|
||||
package net.corda.bridge
|
||||
|
||||
import joptsimple.OptionParser
|
||||
import joptsimple.util.EnumConverter
|
||||
import net.corda.bridge.services.api.FirewallConfiguration
|
||||
import net.corda.bridge.services.config.BridgeConfigHelper
|
||||
import net.corda.bridge.services.config.parseAsFirewallConfiguration
|
||||
import net.corda.core.internal.div
|
||||
import org.slf4j.event.Level
|
||||
import java.io.PrintStream
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
// NOTE: Do not use any logger in this class as args parsing is done before the logger is setup.
|
||||
class ArgsParser {
|
||||
private val optionParser = OptionParser()
|
||||
// The intent of allowing a command line configurable directory and config path is to allow deployment flexibility.
|
||||
// Other general configuration should live inside the config file unless we regularly need temporary overrides on the command line
|
||||
private val baseDirectoryArg = optionParser
|
||||
.accepts("base-directory", "The firewall working directory where all the files are kept")
|
||||
.withRequiredArg()
|
||||
.defaultsTo(".")
|
||||
private val configFileArg = optionParser
|
||||
.accepts("config-file", "The path to the config file")
|
||||
.withRequiredArg()
|
||||
.defaultsTo("firewall.conf")
|
||||
private val loggerLevel = optionParser
|
||||
.accepts("logging-level", "Enable logging at this level and higher")
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(object : EnumConverter<Level>(Level::class.java) {})
|
||||
.defaultsTo(Level.INFO)
|
||||
private val logToConsoleArg = optionParser.accepts("log-to-console", "If set, prints logging to the console as well as to a file.")
|
||||
private val isVersionArg = optionParser.accepts("version", "Print the version and exit")
|
||||
private val helpArg = optionParser.accepts("help").forHelp()
|
||||
|
||||
fun parse(vararg args: String): CmdLineOptions {
|
||||
val optionSet = optionParser.parse(*args)
|
||||
val baseDirectory = Paths.get(optionSet.valueOf(baseDirectoryArg)).normalize().toAbsolutePath()
|
||||
val configFilePath = Paths.get(optionSet.valueOf(configFileArg))
|
||||
val configFile = if (configFilePath.isAbsolute) configFilePath else baseDirectory / configFilePath.toString()
|
||||
val help = optionSet.has(helpArg)
|
||||
val loggingLevel = optionSet.valueOf(loggerLevel)
|
||||
val logToConsole = optionSet.has(logToConsoleArg)
|
||||
val isVersion = optionSet.has(isVersionArg)
|
||||
return CmdLineOptions(baseDirectory,
|
||||
configFile,
|
||||
help,
|
||||
loggingLevel,
|
||||
logToConsole,
|
||||
isVersion)
|
||||
}
|
||||
|
||||
fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink)
|
||||
}
|
||||
|
||||
data class CmdLineOptions(val baseDirectory: Path,
|
||||
val configFile: Path,
|
||||
val help: Boolean,
|
||||
val loggingLevel: Level,
|
||||
val logToConsole: Boolean,
|
||||
val isVersion: Boolean) {
|
||||
fun loadConfig(): FirewallConfiguration {
|
||||
val config = BridgeConfigHelper.loadConfig(baseDirectory, configFile).parseAsFirewallConfiguration()
|
||||
return config
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package net.corda.bridge
|
||||
|
||||
import net.corda.bridge.services.api.FirewallConfiguration
|
||||
import net.corda.bridge.services.config.BridgeConfigHelper
|
||||
import net.corda.bridge.services.config.parseAsFirewallConfiguration
|
||||
import net.corda.core.internal.div
|
||||
import picocli.CommandLine.Option
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
class FirewallCmdLineOptions {
|
||||
@Option(
|
||||
names = ["-b", "--base-directory"],
|
||||
description = ["The firewall working directory where all the files are kept."]
|
||||
)
|
||||
var baseDirectory: Path = Paths.get(".").toAbsolutePath().normalize()
|
||||
|
||||
@Option(
|
||||
names = ["-f", "--config-file"],
|
||||
description = ["The path to the config file. By default this is firewall.conf in the base directory."]
|
||||
)
|
||||
private var _configFile: Path? = null
|
||||
val configFile: Path get() = _configFile ?: (baseDirectory / "firewall.conf")
|
||||
|
||||
fun loadConfig(): FirewallConfiguration {
|
||||
return BridgeConfigHelper.loadConfig(baseDirectory, configFile).parseAsFirewallConfiguration()
|
||||
}
|
||||
}
|
@ -1,74 +1,64 @@
|
||||
package net.corda.bridge.internal
|
||||
|
||||
import com.jcabi.manifests.Manifests
|
||||
import joptsimple.OptionException
|
||||
import net.corda.bridge.ArgsParser
|
||||
import net.corda.bridge.CmdLineOptions
|
||||
import net.corda.bridge.FirewallCmdLineOptions
|
||||
import net.corda.bridge.FirewallVersionInfo
|
||||
import net.corda.bridge.services.api.FirewallConfiguration
|
||||
import net.corda.cliutils.CordaCliWrapper
|
||||
import net.corda.cliutils.CordaVersionProvider
|
||||
import net.corda.cliutils.ExitCodes
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.addShutdownHook
|
||||
import org.slf4j.bridge.SLF4JBridgeHandler
|
||||
import picocli.CommandLine.Mixin
|
||||
import sun.misc.VMSupport
|
||||
import java.io.RandomAccessFile
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.net.InetAddress
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
class FirewallStartup(val args: Array<String>) {
|
||||
class FirewallStartup: CordaCliWrapper("corda-firewall", "The Corda Firewall application for handling outbound and inbound connections to Corda.") {
|
||||
companion object {
|
||||
// lazy init the logging, because the logging levels aren't configured until we have parsed some options.
|
||||
private val log by lazy { contextLogger() }
|
||||
val LOGS_DIRECTORY_NAME = "logs"
|
||||
}
|
||||
|
||||
@Mixin
|
||||
val cmdLineOptions = FirewallCmdLineOptions()
|
||||
|
||||
/**
|
||||
* @return true if the firewalls startup was successful. This value is intended to be the exit code of the process.
|
||||
* @return zero if the firewalls startup was successful. This value is the exit code of the process.
|
||||
*/
|
||||
fun run(): Boolean {
|
||||
override fun runProgram(): Int {
|
||||
val startTime = System.currentTimeMillis()
|
||||
val (argsParser, cmdlineOptions) = parseArguments()
|
||||
|
||||
// We do the single firewall check before we initialise logging so that in case of a double-firewall start it
|
||||
// doesn't mess with the running firewall's logs.
|
||||
enforceSingleBridgeIsRunning(cmdlineOptions.baseDirectory)
|
||||
enforceSingleBridgeIsRunning(cmdLineOptions.baseDirectory)
|
||||
|
||||
initLogging(cmdlineOptions)
|
||||
initLogging()
|
||||
|
||||
val versionInfo = getVersionInfo()
|
||||
|
||||
if (cmdlineOptions.isVersion) {
|
||||
println("${versionInfo.vendor} ${versionInfo.releaseVersion}")
|
||||
println("Revision ${versionInfo.revision}")
|
||||
println("Platform Version ${versionInfo.platformVersion}")
|
||||
return true
|
||||
}
|
||||
|
||||
// Maybe render command line help.
|
||||
if (cmdlineOptions.help) {
|
||||
argsParser.printHelp(System.out)
|
||||
return true
|
||||
}
|
||||
val conf = try {
|
||||
loadConfigFile(cmdlineOptions)
|
||||
loadConfigFile()
|
||||
} catch (e: Exception) {
|
||||
log.error("Exception during firewall configuration", e)
|
||||
return false
|
||||
return ExitCodes.FAILURE
|
||||
}
|
||||
|
||||
try {
|
||||
logStartupInfo(versionInfo, cmdlineOptions, conf)
|
||||
logStartupInfo(versionInfo, conf)
|
||||
} catch (e: Exception) {
|
||||
log.error("Exception during firewall registration", e)
|
||||
return false
|
||||
return ExitCodes.FAILURE
|
||||
}
|
||||
|
||||
val firewall = try {
|
||||
cmdlineOptions.baseDirectory.createDirectories()
|
||||
cmdLineOptions.baseDirectory.createDirectories()
|
||||
startFirewall(conf, versionInfo, startTime)
|
||||
} catch (e: Exception) {
|
||||
if (e.message?.startsWith("Unknown named curve:") == true) {
|
||||
@ -77,7 +67,7 @@ class FirewallStartup(val args: Array<String>) {
|
||||
} else {
|
||||
log.error("Exception during firewall startup", e)
|
||||
}
|
||||
return false
|
||||
return ExitCodes.FAILURE
|
||||
}
|
||||
|
||||
if (System.getProperties().containsKey("WAIT_KEY_FOR_EXIT")) {
|
||||
@ -89,10 +79,10 @@ class FirewallStartup(val args: Array<String>) {
|
||||
log.info("firewall shutting down")
|
||||
firewall.stop()
|
||||
|
||||
return true
|
||||
return ExitCodes.SUCCESS
|
||||
}
|
||||
|
||||
fun logStartupInfo(versionInfo: FirewallVersionInfo, cmdlineOptions: CmdLineOptions, conf: FirewallConfiguration) {
|
||||
fun logStartupInfo(versionInfo: FirewallVersionInfo, conf: FirewallConfiguration) {
|
||||
log.info("Vendor: ${versionInfo.vendor}")
|
||||
log.info("Release: ${versionInfo.releaseVersion}")
|
||||
log.info("Platform Version: ${versionInfo.platformVersion}")
|
||||
@ -106,7 +96,7 @@ class FirewallStartup(val args: Array<String>) {
|
||||
log.info("classpath: ${info.classPath}")
|
||||
log.info("VM ${info.vmName} ${info.vmVendor} ${info.vmVersion}")
|
||||
log.info("Machine: ${lookupMachineNameAndMaybeWarn()}")
|
||||
log.info("Working Directory: ${cmdlineOptions.baseDirectory}")
|
||||
log.info("Working Directory: ${cmdLineOptions.baseDirectory}")
|
||||
val agentProperties = VMSupport.getAgentProperties()
|
||||
if (agentProperties.containsKey("sun.jdwp.listenerAddress")) {
|
||||
log.info("Debug port: ${agentProperties.getProperty("sun.jdwp.listenerAddress")}")
|
||||
@ -114,20 +104,16 @@ class FirewallStartup(val args: Array<String>) {
|
||||
log.info("Starting as firewall mode of ${conf.firewallMode}")
|
||||
}
|
||||
|
||||
protected fun loadConfigFile(cmdlineOptions: CmdLineOptions): FirewallConfiguration = cmdlineOptions.loadConfig()
|
||||
protected fun loadConfigFile(): FirewallConfiguration = cmdLineOptions.loadConfig()
|
||||
|
||||
protected fun getVersionInfo(): FirewallVersionInfo {
|
||||
// Manifest properties are only available if running from the corda jar
|
||||
fun manifestValue(name: String): String? = if (Manifests.exists(name)) Manifests.read(name) else null
|
||||
|
||||
return FirewallVersionInfo(
|
||||
manifestValue("Corda-Platform-Version")?.toInt() ?: 1,
|
||||
manifestValue("Corda-Release-Version") ?: "Unknown",
|
||||
manifestValue("Corda-Revision") ?: "Unknown",
|
||||
manifestValue("Corda-Vendor") ?: "Unknown"
|
||||
CordaVersionProvider.platformVersion,
|
||||
CordaVersionProvider.releaseVersion,
|
||||
CordaVersionProvider.revision,
|
||||
CordaVersionProvider.vendor
|
||||
)
|
||||
}
|
||||
|
||||
private fun enforceSingleBridgeIsRunning(baseDirectory: Path) {
|
||||
// Write out our process ID (which may or may not resemble a UNIX process id - to us it's just a string) to a
|
||||
// file that we'll do our best to delete on exit. But if we don't, it'll be overwritten next time. If it already
|
||||
@ -171,25 +157,13 @@ class FirewallStartup(val args: Array<String>) {
|
||||
return hostName
|
||||
}
|
||||
|
||||
private fun parseArguments(): Pair<ArgsParser, CmdLineOptions> {
|
||||
val argsParser = ArgsParser()
|
||||
val cmdlineOptions = try {
|
||||
argsParser.parse(*args)
|
||||
} catch (ex: OptionException) {
|
||||
println("Invalid command line arguments: ${ex.message}")
|
||||
argsParser.printHelp(System.out)
|
||||
exitProcess(1)
|
||||
}
|
||||
return Pair(argsParser, cmdlineOptions)
|
||||
}
|
||||
|
||||
fun initLogging(cmdlineOptions: CmdLineOptions) {
|
||||
val loggingLevel = cmdlineOptions.loggingLevel.name.toLowerCase(Locale.ENGLISH)
|
||||
override fun initLogging() {
|
||||
val loggingLevel = loggingLevel.name.toLowerCase(Locale.ENGLISH)
|
||||
System.setProperty("defaultLogLevel", loggingLevel) // These properties are referenced from the XML config file.
|
||||
if (cmdlineOptions.logToConsole) {
|
||||
if (verbose) {
|
||||
System.setProperty("consoleLogLevel", loggingLevel)
|
||||
}
|
||||
System.setProperty("log-path", (cmdlineOptions.baseDirectory / LOGS_DIRECTORY_NAME).toString())
|
||||
System.setProperty("log-path", (cmdLineOptions.baseDirectory / LOGS_DIRECTORY_NAME).toString())
|
||||
SLF4JBridgeHandler.removeHandlersForRootLogger() // The default j.u.l config adds a ConsoleHandler.
|
||||
SLF4JBridgeHandler.install()
|
||||
}
|
||||
|
@ -42,15 +42,14 @@ fun createNetworkParams(baseDirectory: Path): Int {
|
||||
|
||||
fun createAndLoadConfigFromResource(baseDirectory: Path, configResource: String): FirewallConfiguration {
|
||||
val workspaceFolder = baseDirectory.normalize().toAbsolutePath()
|
||||
val args = arrayOf("--base-directory", workspaceFolder.toString())
|
||||
val argsParser = ArgsParser()
|
||||
val cmdlineOptions = argsParser.parse(*args)
|
||||
val configFile = cmdlineOptions.configFile
|
||||
val cmdLineOptions = FirewallCmdLineOptions()
|
||||
cmdLineOptions.baseDirectory = workspaceFolder
|
||||
val configFile = cmdLineOptions.configFile
|
||||
configFile.normalize().parent?.createDirectories()
|
||||
ConfigTest::class.java.getResourceAsStream(configResource).use {
|
||||
Files.copy(it, configFile)
|
||||
}
|
||||
val config = cmdlineOptions.loadConfig()
|
||||
val config = cmdLineOptions.loadConfig()
|
||||
return config
|
||||
}
|
||||
|
||||
|
@ -74,3 +74,11 @@ List of existing CLI applications
|
||||
| :doc:`Blob inspector<blob-inspector>` | ``corda-tools-blob-inspector-<version>.jar`` | ``blob-inspector --<option>`` |
|
||||
+----------------------------------------------------------------+--------------------------------------------------------------+--------------------------------+
|
||||
|
||||
List of existing Enterprise CLI applications
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
+----------------------------------------------------------------+--------------------------------------------------------------+--------------------------------+
|
||||
| Description | JAR name | Alias |
|
||||
+----------------------------------------------------------------+--------------------------------------------------------------+--------------------------------+
|
||||
| :ref:`Corda Firewall<firewall-coniguration-file>` | ``corda-firewall-<version>.jar`` | ``corda-firewall --<option>`` |
|
||||
+----------------------------------------------------------------+--------------------------------------------------------------+--------------------------------+
|
@ -6,16 +6,26 @@ Firewall configuration
|
||||
File location
|
||||
-------------
|
||||
When starting a standalone firewall (in bridge, or float mode), the ``corda-firewall.jar`` file defaults to reading the firewall's configuration from a ``firewall.conf`` file in
|
||||
the directory from which the command to launch the process is executed. There are two command-line options to override this
|
||||
behaviour:
|
||||
the directory from which the command to launch the process is executed. The syntax is:
|
||||
|
||||
* The ``--config-file`` command line option allows you to specify a configuration file with a different name, or at
|
||||
different file location. Paths are relative to the current working directory
|
||||
|
||||
* The ``--base-directory`` command line option allows you to specify the firewall's workspace location. A ``firewall.conf``
|
||||
.. code:: bash
|
||||
|
||||
corda-firewall [-hvV] [--install-shell-extensions]
|
||||
[--logging-level=<loggingLevel>] [-b=<baseDirectory>]
|
||||
[-f=<_configFile>]
|
||||
|
||||
Where:
|
||||
|
||||
* ``--config-file``, ``-f``: Allows you to specify a configuration file with a different name, or at
|
||||
a different file location. Paths are relative to the current working directory
|
||||
* ``--base-directory``, ``-b``: Allows you to specify the firewall's workspace location. A ``firewall.conf``
|
||||
configuration file is then expected in the root of this workspace
|
||||
|
||||
If you specify both command line arguments at the same time, the firewall will fail to start.
|
||||
* ``--verbose``, ``--log-to-console``, ``-v``: If set, prints logging to the console as well as to a file.
|
||||
* ``--logging-level=<loggingLevel>``: Enable logging at this level and higher. Possible values: ERROR, WARN, INFO, DEBUG, TRACE. Default: INFO.
|
||||
* ``--install-shell-extensions``: Install ``corda-firewall`` alias and auto completion for bash and zsh. See :doc:`cli-application-shell-extensions` for more info.
|
||||
* ``--help``, ``-h``: Show this help message and exit.
|
||||
* ``--version``, ``-V``: Print version information and exit.
|
||||
|
||||
Format
|
||||
------
|
||||
|
Loading…
x
Reference in New Issue
Block a user