mirror of
https://github.com/corda/corda.git
synced 2025-06-13 04:38:19 +00:00
CORDA-1764: Make shell use picocli for parsing command line options (#3923)
* Fix link in shell documentation
* The TypeSafe config parser wants extensions.sshd to be present in the config even though extensions is nullable
* Temp commit
* Make Standalone Shell use picocli
* Simplify gradle config for bootstrapper
* Fix logging dependency issues
* Revert "Temp commit"
This reverts commit f4efafcc9d
.
* Fix quasarExcludeExpression
* Correct bootstrapper configuration
* Correct CRaSH capitalisation in docs
* Fix unit tests
* Fix help text typo
* Make logging level case insensitive
* Fix CRaSH capitalisation in help text
* Fix unit tests
This commit is contained in:
@ -125,7 +125,7 @@ Where:
|
|||||||
|
|
||||||
* ``config-file`` is the path to config file, used instead of providing the rest of command line options
|
* ``config-file`` is the path to config file, used instead of providing the rest of command line options
|
||||||
* ``cordpass-directory`` is the directory containing Cordapps jars, Cordapps are require when starting flows
|
* ``cordpass-directory`` is the directory containing Cordapps jars, Cordapps are require when starting flows
|
||||||
* ``commands-directory`` is the directory with additional CrAsH shell commands
|
* ``commands-directory`` is the directory with additional CRaSH shell commands
|
||||||
* ``host`` is the Corda node's host
|
* ``host`` is the Corda node's host
|
||||||
* ``port`` is the Corda node's port, specified in the ``node.conf`` file
|
* ``port`` is the Corda node's port, specified in the ``node.conf`` file
|
||||||
* ``user`` is the RPC username, if not provided it will be requested at startup
|
* ``user`` is the RPC username, if not provided it will be requested at startup
|
||||||
|
@ -46,7 +46,7 @@ task buildCordaJAR(type: FatCapsule, dependsOn: project(':node').tasks.jar) {
|
|||||||
applicationVersion = corda_release_version
|
applicationVersion = corda_release_version
|
||||||
|
|
||||||
// See experimental/quasar-hook/README.md for how to generate.
|
// See experimental/quasar-hook/README.md for how to generate.
|
||||||
def quasarExcludeExpression = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**)"
|
def quasarExcludeExpression = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**)"
|
||||||
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar=${quasarExcludeExpression}"]
|
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar=${quasarExcludeExpression}"]
|
||||||
systemProperties['visualvm.display.name'] = 'Corda'
|
systemProperties['visualvm.display.name'] = 'Corda'
|
||||||
minJavaVersion = '1.8.0'
|
minJavaVersion = '1.8.0'
|
||||||
|
@ -456,7 +456,7 @@ open class NodeStartup: CordaCliWrapper("corda", "Runs a Corda Node") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun initLogging() {
|
override fun initLogging() {
|
||||||
val loggingLevel = loggingLevel.name().toLowerCase(Locale.ENGLISH)
|
val loggingLevel = loggingLevel.name.toLowerCase(Locale.ENGLISH)
|
||||||
System.setProperty("defaultLogLevel", loggingLevel) // These properties are referenced from the XML config file.
|
System.setProperty("defaultLogLevel", loggingLevel) // These properties are referenced from the XML config file.
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
System.setProperty("consoleLogLevel", loggingLevel)
|
System.setProperty("consoleLogLevel", loggingLevel)
|
||||||
|
@ -3,10 +3,10 @@ package net.corda.node
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.node.internal.NodeStartup
|
import net.corda.node.internal.NodeStartup
|
||||||
import net.corda.nodeapi.internal.config.UnknownConfigKeysPolicy
|
import net.corda.nodeapi.internal.config.UnknownConfigKeysPolicy
|
||||||
import org.apache.logging.log4j.Level
|
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.BeforeClass
|
import org.junit.BeforeClass
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import org.slf4j.event.Level
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
apply plugin: 'java'
|
|
||||||
apply plugin: 'kotlin'
|
apply plugin: 'kotlin'
|
||||||
apply plugin: 'net.corda.plugins.publish-utils'
|
apply plugin: 'net.corda.plugins.publish-utils'
|
||||||
apply plugin: 'com.jfrog.artifactory'
|
apply plugin: 'com.jfrog.artifactory'
|
||||||
@ -8,14 +7,15 @@ description 'Network bootstrapper'
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile project(':node-api')
|
compile project(':node-api')
|
||||||
compile project(':tools:cliutils')
|
compile project(':tools:cliutils')
|
||||||
compile "info.picocli:picocli:$picocli_version"
|
|
||||||
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
|
|
||||||
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
||||||
compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
from file("$rootDir/config/dev/log4j2.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {
|
from(configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }) {
|
||||||
exclude "META-INF/*.SF"
|
exclude "META-INF/*.SF"
|
||||||
exclude "META-INF/*.DSA"
|
exclude "META-INF/*.DSA"
|
||||||
exclude "META-INF/*.RSA"
|
exclude "META-INF/*.RSA"
|
||||||
@ -23,10 +23,9 @@ jar {
|
|||||||
from(project(':node:capsule').tasks['buildCordaJAR']) {
|
from(project(':node:capsule').tasks['buildCordaJAR']) {
|
||||||
rename 'corda-(.*)', 'corda.jar'
|
rename 'corda-(.*)', 'corda.jar'
|
||||||
}
|
}
|
||||||
archiveName = "network-bootstrapper-${corda_release_version}.jar"
|
baseName = "network-bootstrapper"
|
||||||
manifest {
|
manifest {
|
||||||
attributes(
|
attributes(
|
||||||
'Automatic-Module-Name': 'net.corda.bootstrapper',
|
|
||||||
'Main-Class': 'net.corda.bootstrapper.MainKt'
|
'Main-Class': 'net.corda.bootstrapper.MainKt'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Configuration status="info">
|
|
||||||
<Properties>
|
|
||||||
<Property name="logLevel">off</Property>
|
|
||||||
</Properties>
|
|
||||||
<Appenders>
|
|
||||||
<Console name="STDOUT" target="SYSTEM_OUT" ignoreExceptions="false">
|
|
||||||
<PatternLayout pattern="[%C{1}.%M] %m%n"/>
|
|
||||||
</Console>
|
|
||||||
</Appenders>
|
|
||||||
<Loggers>
|
|
||||||
<Root level="${sys:logLevel}">
|
|
||||||
<AppenderRef ref="STDOUT"/>
|
|
||||||
</Root>
|
|
||||||
</Loggers>
|
|
||||||
</Configuration>
|
|
@ -10,7 +10,7 @@ dependencies {
|
|||||||
|
|
||||||
compile "info.picocli:picocli:$picocli_version"
|
compile "info.picocli:picocli:$picocli_version"
|
||||||
compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
|
compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
|
||||||
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
compile "org.slf4j:slf4j-api:$slf4j_version"
|
||||||
|
|
||||||
// JAnsi: for drawing things to the terminal in nicely coloured ways.
|
// JAnsi: for drawing things to the terminal in nicely coloured ways.
|
||||||
compile "org.fusesource.jansi:jansi:$jansi_version"
|
compile "org.fusesource.jansi:jansi:$jansi_version"
|
||||||
|
@ -4,10 +4,11 @@ import net.corda.core.internal.rootMessage
|
|||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.loggerFor
|
import net.corda.core.utilities.loggerFor
|
||||||
|
|
||||||
import org.apache.logging.log4j.Level
|
|
||||||
import org.fusesource.jansi.AnsiConsole
|
import org.fusesource.jansi.AnsiConsole
|
||||||
|
import org.slf4j.event.Level
|
||||||
import picocli.CommandLine
|
import picocli.CommandLine
|
||||||
import picocli.CommandLine.*
|
import picocli.CommandLine.*
|
||||||
|
import java.nio.file.Paths
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Callable
|
import java.util.concurrent.Callable
|
||||||
@ -127,11 +128,12 @@ abstract class CordaCliWrapper(val alias: String, val description: String) : Cal
|
|||||||
// This needs to be called before loggers (See: NodeStartup.kt:51 logger called by lazy, initLogging happens before).
|
// This needs to be called before loggers (See: NodeStartup.kt:51 logger called by lazy, initLogging happens before).
|
||||||
// Node's logging is more rich. In corda configurations two properties, defaultLoggingLevel and consoleLogLevel, are usually used.
|
// Node's logging is more rich. In corda configurations two properties, defaultLoggingLevel and consoleLogLevel, are usually used.
|
||||||
open fun initLogging() {
|
open fun initLogging() {
|
||||||
val loggingLevel = loggingLevel.name().toLowerCase(Locale.ENGLISH)
|
val loggingLevel = loggingLevel.name.toLowerCase(Locale.ENGLISH)
|
||||||
System.setProperty("defaultLogLevel", loggingLevel) // These properties are referenced from the XML config file.
|
System.setProperty("defaultLogLevel", loggingLevel) // These properties are referenced from the XML config file.
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
System.setProperty("consoleLogLevel", loggingLevel)
|
System.setProperty("consoleLogLevel", loggingLevel)
|
||||||
}
|
}
|
||||||
|
System.setProperty("log-path", Paths.get(".").toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override this function with the actual method to be run once all the arguments have been parsed. The return number
|
// Override this function with the actual method to be run once all the arguments have been parsed. The return number
|
||||||
@ -147,11 +149,11 @@ abstract class CordaCliWrapper(val alias: String, val description: String) : Cal
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converter from String to log4j logging Level.
|
* Converter from String to slf4j logging Level.
|
||||||
*/
|
*/
|
||||||
class LoggingLevelConverter : ITypeConverter<Level> {
|
class LoggingLevelConverter : ITypeConverter<Level> {
|
||||||
override fun convert(value: String?): Level {
|
override fun convert(value: String?): Level {
|
||||||
return value?.let { Level.getLevel(it) }
|
return value?.let { Level.valueOf(it.toUpperCase()) }
|
||||||
?: throw TypeConversionException("Unknown option for --logging-level: $value")
|
?: throw TypeConversionException("Unknown option for --logging-level: $value")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Standalone Shell
|
Standalone Shell
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
Documentation for shell CLI can be found [here](http://docs.corda.net/website/releases/docs_head/shell.html)
|
Documentation for the standalone shell can be found [here](https://docs.corda.net/head/shell.html#the-standalone-shell)
|
||||||
|
|
||||||
To build this from the command line on Unix or MacOS:
|
To build this from the command line on Unix or MacOS:
|
||||||
|
|
||||||
|
@ -10,7 +10,9 @@ apply plugin: 'com.jfrog.artifactory'
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':tools:shell')
|
compile project(':tools:shell')
|
||||||
compile "org.slf4j:slf4j-simple:$slf4j_version"
|
compile project(':tools:cliutils')
|
||||||
|
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
||||||
|
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
|
||||||
|
|
||||||
testCompile(project(':test-utils')) {
|
testCompile(project(':test-utils')) {
|
||||||
exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl'
|
exclude group: 'org.apache.logging.log4j', module: 'log4j-slf4j-impl'
|
||||||
|
@ -0,0 +1,195 @@
|
|||||||
|
package net.corda.tools.shell
|
||||||
|
|
||||||
|
import com.typesafe.config.Config
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.core.messaging.ClientRpcSslOptions
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.nodeapi.internal.config.parseAs
|
||||||
|
import net.corda.tools.shell.ShellConfiguration.Companion.COMMANDS_DIR
|
||||||
|
import picocli.CommandLine.Option
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
class ShellCmdLineOptions {
|
||||||
|
@Option(
|
||||||
|
names = ["-f", "--config-file"],
|
||||||
|
description = ["The path to the shell configuration file, used instead of providing the rest of command line options."]
|
||||||
|
)
|
||||||
|
var configFile: Path? = null
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-c", "--cordapp-directory"],
|
||||||
|
description = ["The path to the directory containing CorDapp JARs, CorDapps are required when starting flows."]
|
||||||
|
)
|
||||||
|
var cordappDirectory: Path? = null
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-o", "--commands-directory"],
|
||||||
|
description = ["The path to the directory containing additional CRaSH shell commands."]
|
||||||
|
)
|
||||||
|
var commandsDirectory: Path? = null
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-a", "--host"],
|
||||||
|
description = ["The host address of the Corda node."]
|
||||||
|
)
|
||||||
|
var host: String? = null
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["-p", "--port"],
|
||||||
|
description = ["The RPC port of the Corda node."]
|
||||||
|
)
|
||||||
|
var port: String? = null
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["--user"],
|
||||||
|
description = ["The RPC user name."]
|
||||||
|
)
|
||||||
|
var user: String? = null
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["--password"],
|
||||||
|
description = ["The RPC user password."]
|
||||||
|
)
|
||||||
|
var password: String? = null
|
||||||
|
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["--sshd-port"],
|
||||||
|
description = ["Enables SSH server for shell."]
|
||||||
|
)
|
||||||
|
var sshdPort: String? = null
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["--sshd-hostkey-directory"],
|
||||||
|
description = ["The directory with hostkey.pem file for SSH server."]
|
||||||
|
)
|
||||||
|
var sshdHostKeyDirectory: Path? = null
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["--truststore-password"],
|
||||||
|
description = ["The password to unlock the TrustStore file."]
|
||||||
|
)
|
||||||
|
var trustStorePassword: String? = null
|
||||||
|
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["--truststore-file"],
|
||||||
|
description = ["The path to the TrustStore file."]
|
||||||
|
)
|
||||||
|
var trustStoreFile: Path? = null
|
||||||
|
|
||||||
|
|
||||||
|
@Option(
|
||||||
|
names = ["--truststore-type"],
|
||||||
|
description = ["The type of the TrustStore (e.g. JKS)."]
|
||||||
|
)
|
||||||
|
var trustStoreType: String? = null
|
||||||
|
|
||||||
|
|
||||||
|
private fun toConfigFile(): Config {
|
||||||
|
val cmdOpts = mutableMapOf<String, Any?>()
|
||||||
|
|
||||||
|
commandsDirectory?.apply { cmdOpts["extensions.commands.path"] = this.toString() }
|
||||||
|
cordappDirectory?.apply { cmdOpts["extensions.cordapps.path"] = this.toString() }
|
||||||
|
user?.apply { cmdOpts["node.user"] = this }
|
||||||
|
password?.apply { cmdOpts["node.password"] = this }
|
||||||
|
host?.apply { cmdOpts["node.addresses.rpc.host"] = this }
|
||||||
|
port?.apply { cmdOpts["node.addresses.rpc.port"] = this }
|
||||||
|
trustStoreFile?.apply { cmdOpts["ssl.truststore.path"] = this.toString() }
|
||||||
|
trustStorePassword?.apply { cmdOpts["ssl.truststore.password"] = this }
|
||||||
|
trustStoreType?.apply { cmdOpts["ssl.truststore.type"] = this }
|
||||||
|
sshdPort?.apply {
|
||||||
|
cmdOpts["extensions.sshd.port"] = this
|
||||||
|
cmdOpts["extensions.sshd.enabled"] = true
|
||||||
|
}
|
||||||
|
sshdHostKeyDirectory?.apply { cmdOpts["extensions.sshd.hostkeypath"] = this.toString() }
|
||||||
|
|
||||||
|
return ConfigFactory.parseMap(cmdOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return configuration parsed from an optional config file (provided by the command line option)
|
||||||
|
* and then overridden by the command line options */
|
||||||
|
fun toConfig(): ShellConfiguration {
|
||||||
|
val fileConfig = configFile?.let { ConfigFactory.parseFile(it.toFile()) }
|
||||||
|
?: ConfigFactory.empty()
|
||||||
|
val typeSafeConfig = toConfigFile().withFallback(fileConfig).resolve()
|
||||||
|
val shellConfigFile = typeSafeConfig.parseAs<ShellConfigurationFile.ShellConfigFile>()
|
||||||
|
return shellConfigFile.toShellConfiguration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Object representation of Shell configuration file */
|
||||||
|
private class ShellConfigurationFile {
|
||||||
|
data class Rpc(
|
||||||
|
val host: String,
|
||||||
|
val port: Int)
|
||||||
|
|
||||||
|
data class Addresses(
|
||||||
|
val rpc: Rpc
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Node(
|
||||||
|
val addresses: Addresses,
|
||||||
|
val user: String?,
|
||||||
|
val password: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Cordapps(
|
||||||
|
val path: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Sshd(
|
||||||
|
val enabled: Boolean,
|
||||||
|
val port: Int,
|
||||||
|
val hostkeypath: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Commands(
|
||||||
|
val path: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Extensions(
|
||||||
|
val cordapps: Cordapps?,
|
||||||
|
val sshd: Sshd?,
|
||||||
|
val commands: Commands?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class KeyStore(
|
||||||
|
val path: String,
|
||||||
|
val type: String = "JKS",
|
||||||
|
val password: String
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Ssl(
|
||||||
|
val truststore: KeyStore
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ShellConfigFile(
|
||||||
|
val node: Node,
|
||||||
|
val extensions: Extensions?,
|
||||||
|
val ssl: Ssl?
|
||||||
|
) {
|
||||||
|
fun toShellConfiguration(): ShellConfiguration {
|
||||||
|
|
||||||
|
val sslOptions =
|
||||||
|
ssl?.let {
|
||||||
|
ClientRpcSslOptions(
|
||||||
|
trustStorePath = Paths.get(it.truststore.path),
|
||||||
|
trustStorePassword = it.truststore.password)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShellConfiguration(
|
||||||
|
commandsDirectory = extensions?.commands?.let { Paths.get(it.path) } ?: Paths.get(".")
|
||||||
|
/ COMMANDS_DIR,
|
||||||
|
cordappsDirectory = extensions?.cordapps?.let { Paths.get(it.path) },
|
||||||
|
user = node.user ?: "",
|
||||||
|
password = node.password ?: "",
|
||||||
|
hostAndPort = NetworkHostAndPort(node.addresses.rpc.host, node.addresses.rpc.port),
|
||||||
|
ssl = sslOptions,
|
||||||
|
sshdPort = extensions?.sshd?.let { if (it.enabled) it.port else null },
|
||||||
|
sshHostKeyDirectory = extensions?.sshd?.let { if (it.enabled && it.hostkeypath != null) Paths.get(it.hostkeypath) else null })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,45 +1,34 @@
|
|||||||
package net.corda.tools.shell
|
package net.corda.tools.shell
|
||||||
|
|
||||||
import com.jcabi.manifests.Manifests
|
import com.jcabi.manifests.Manifests
|
||||||
import joptsimple.OptionException
|
import net.corda.cliutils.CordaCliWrapper
|
||||||
import net.corda.core.internal.*
|
import net.corda.cliutils.ExitCodes
|
||||||
|
import net.corda.cliutils.start
|
||||||
|
import net.corda.core.internal.exists
|
||||||
|
import net.corda.core.internal.isRegularFile
|
||||||
|
import net.corda.core.internal.list
|
||||||
import org.fusesource.jansi.Ansi
|
import org.fusesource.jansi.Ansi
|
||||||
import org.fusesource.jansi.AnsiConsole
|
import org.fusesource.jansi.AnsiConsole
|
||||||
|
import org.slf4j.bridge.SLF4JBridgeHandler
|
||||||
|
import picocli.CommandLine.Mixin
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import kotlin.streams.toList
|
import kotlin.streams.toList
|
||||||
import java.io.IOException
|
|
||||||
import java.io.BufferedReader
|
|
||||||
import java.io.InputStreamReader
|
|
||||||
import kotlin.system.exitProcess
|
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
|
StandaloneShell().start(args)
|
||||||
val argsParser = CommandLineOptionParser()
|
|
||||||
val cmdlineOptions = try {
|
|
||||||
argsParser.parse(*args)
|
|
||||||
} catch (e: OptionException) {
|
|
||||||
println("Invalid command line arguments: ${e.message}")
|
|
||||||
argsParser.printHelp(System.out)
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmdlineOptions.help) {
|
|
||||||
argsParser.printHelp(System.out)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val config = try {
|
|
||||||
cmdlineOptions.toConfig()
|
|
||||||
} catch(e: Exception) {
|
|
||||||
println("Configuration exception: ${e.message}")
|
|
||||||
exitProcess(1)
|
|
||||||
}
|
|
||||||
StandaloneShell(config).run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class StandaloneShell(private val configuration: ShellConfiguration) {
|
class StandaloneShell : CordaCliWrapper("corda-shell", "The Corda standalone shell.") {
|
||||||
|
@Mixin
|
||||||
|
var cmdLineOptions = ShellCmdLineOptions()
|
||||||
|
|
||||||
|
lateinit var configuration: ShellConfiguration
|
||||||
|
|
||||||
private fun getCordappsInDirectory(cordappsDir: Path?): List<URL> =
|
private fun getCordappsInDirectory(cordappsDir: Path?): List<URL> =
|
||||||
if (cordappsDir == null || !cordappsDir.exists()) {
|
if (cordappsDir == null || !cordappsDir.exists()) {
|
||||||
@ -67,7 +56,20 @@ class StandaloneShell(private val configuration: ShellConfiguration) {
|
|||||||
|
|
||||||
private fun getManifestEntry(key: String) = if (Manifests.exists(key)) Manifests.read(key) else "Unknown"
|
private fun getManifestEntry(key: String) = if (Manifests.exists(key)) Manifests.read(key) else "Unknown"
|
||||||
|
|
||||||
fun run() {
|
override fun initLogging() {
|
||||||
|
super.initLogging()
|
||||||
|
SLF4JBridgeHandler.removeHandlersForRootLogger() // The default j.u.l config adds a ConsoleHandler.
|
||||||
|
SLF4JBridgeHandler.install()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun runProgram(): Int {
|
||||||
|
configuration = try {
|
||||||
|
cmdLineOptions.toConfig()
|
||||||
|
} catch(e: Exception) {
|
||||||
|
println("Configuration exception: ${e.message}")
|
||||||
|
return ExitCodes.FAILURE
|
||||||
|
}
|
||||||
|
|
||||||
val cordappJarPaths = getCordappsInDirectory(configuration.cordappsDirectory)
|
val cordappJarPaths = getCordappsInDirectory(configuration.cordappsDirectory)
|
||||||
val classLoader: ClassLoader = URLClassLoader(cordappJarPaths.toTypedArray(), javaClass.classLoader)
|
val classLoader: ClassLoader = URLClassLoader(cordappJarPaths.toTypedArray(), javaClass.classLoader)
|
||||||
with(configuration) {
|
with(configuration) {
|
||||||
@ -84,7 +86,7 @@ class StandaloneShell(private val configuration: ShellConfiguration) {
|
|||||||
InteractiveShell.nodeInfo()
|
InteractiveShell.nodeInfo()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println("Cannot login to ${configuration.hostAndPort}, reason: \"${e.message}\"")
|
println("Cannot login to ${configuration.hostAndPort}, reason: \"${e.message}\"")
|
||||||
exitProcess(1)
|
return ExitCodes.FAILURE
|
||||||
}
|
}
|
||||||
|
|
||||||
val exit = CountDownLatch(1)
|
val exit = CountDownLatch(1)
|
||||||
@ -106,6 +108,6 @@ class StandaloneShell(private val configuration: ShellConfiguration) {
|
|||||||
|
|
||||||
exit.await()
|
exit.await()
|
||||||
// because we can't clean certain Crash Shell threads that block on read()
|
// because we can't clean certain Crash Shell threads that block on read()
|
||||||
exitProcess(0)
|
return ExitCodes.SUCCESS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,207 +0,0 @@
|
|||||||
package net.corda.tools.shell
|
|
||||||
|
|
||||||
import com.typesafe.config.Config
|
|
||||||
import com.typesafe.config.ConfigFactory
|
|
||||||
import joptsimple.OptionParser
|
|
||||||
import joptsimple.util.EnumConverter
|
|
||||||
import net.corda.core.internal.div
|
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
|
||||||
import net.corda.core.messaging.ClientRpcSslOptions
|
|
||||||
import net.corda.nodeapi.internal.config.parseAs
|
|
||||||
import net.corda.tools.shell.ShellConfiguration.Companion.COMMANDS_DIR
|
|
||||||
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 CommandLineOptionParser {
|
|
||||||
private val optionParser = OptionParser()
|
|
||||||
|
|
||||||
private val configFileArg = optionParser
|
|
||||||
.accepts("config-file", "The path to the shell configuration file, used instead of providing the rest of command line options.")
|
|
||||||
.withOptionalArg()
|
|
||||||
private val cordappsDirectoryArg = optionParser
|
|
||||||
.accepts("cordpass-directory", "The path to directory containing Cordapps jars, Cordapps are require when starting flows.")
|
|
||||||
.withOptionalArg()
|
|
||||||
private val commandsDirectoryArg = optionParser
|
|
||||||
.accepts("commands-directory", "The directory with additional CrAsH shell commands.")
|
|
||||||
.withOptionalArg()
|
|
||||||
private val hostArg = optionParser
|
|
||||||
.acceptsAll(listOf("h", "host"), "The host of the Corda node.")
|
|
||||||
.withRequiredArg()
|
|
||||||
private val portArg = optionParser
|
|
||||||
.acceptsAll(listOf("p", "port"), "The port of the Corda node.")
|
|
||||||
.withRequiredArg()
|
|
||||||
private val userArg = optionParser
|
|
||||||
.accepts("user", "The RPC user name.")
|
|
||||||
.withOptionalArg()
|
|
||||||
private val passwordArg = optionParser
|
|
||||||
.accepts("password", "The RPC user password.")
|
|
||||||
.withOptionalArg()
|
|
||||||
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 sshdPortArg = optionParser
|
|
||||||
.accepts("sshd-port", "Enables SSH server for shell.")
|
|
||||||
.withOptionalArg()
|
|
||||||
private val sshdHostKeyDirectoryArg = optionParser
|
|
||||||
.accepts("sshd-hostkey-directory", "The directory with hostkey.pem file for SSH server.")
|
|
||||||
.withOptionalArg()
|
|
||||||
private val helpArg = optionParser
|
|
||||||
.accepts("help")
|
|
||||||
.forHelp()
|
|
||||||
private val trustStorePasswordArg = optionParser
|
|
||||||
.accepts("truststore-password", "The password to unlock the TrustStore file.")
|
|
||||||
.withOptionalArg()
|
|
||||||
private val trustStoreDirArg = optionParser
|
|
||||||
.accepts("truststore-file", "The path to the TrustStore file.")
|
|
||||||
.withOptionalArg()
|
|
||||||
private val trustStoreTypeArg = optionParser
|
|
||||||
.accepts("truststore-type", "The type of the TrustStore (e.g. JKS).")
|
|
||||||
.withOptionalArg()
|
|
||||||
|
|
||||||
fun parse(vararg args: String): CommandLineOptions {
|
|
||||||
val optionSet = optionParser.parse(*args)
|
|
||||||
return CommandLineOptions(
|
|
||||||
configFile = optionSet.valueOf(configFileArg),
|
|
||||||
host = optionSet.valueOf(hostArg),
|
|
||||||
port = optionSet.valueOf(portArg),
|
|
||||||
user = optionSet.valueOf(userArg),
|
|
||||||
password = optionSet.valueOf(passwordArg),
|
|
||||||
commandsDirectory = (optionSet.valueOf(commandsDirectoryArg))?.let { Paths.get(it).normalize().toAbsolutePath() },
|
|
||||||
cordappsDirectory = (optionSet.valueOf(cordappsDirectoryArg))?.let { Paths.get(it).normalize().toAbsolutePath() },
|
|
||||||
help = optionSet.has(helpArg),
|
|
||||||
loggingLevel = optionSet.valueOf(loggerLevel),
|
|
||||||
sshdPort = optionSet.valueOf(sshdPortArg),
|
|
||||||
sshdHostKeyDirectory = (optionSet.valueOf(sshdHostKeyDirectoryArg))?.let { Paths.get(it).normalize().toAbsolutePath() },
|
|
||||||
trustStorePassword = optionSet.valueOf(trustStorePasswordArg),
|
|
||||||
trustStoreFile = (optionSet.valueOf(trustStoreDirArg))?.let { Paths.get(it).normalize().toAbsolutePath() },
|
|
||||||
trustStoreType = optionSet.valueOf(trustStoreTypeArg))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun printHelp(sink: PrintStream) = optionParser.printHelpOn(sink)
|
|
||||||
}
|
|
||||||
|
|
||||||
data class CommandLineOptions(val configFile: String?,
|
|
||||||
val commandsDirectory: Path?,
|
|
||||||
val cordappsDirectory: Path?,
|
|
||||||
val host: String?,
|
|
||||||
val port: String?,
|
|
||||||
val user: String?,
|
|
||||||
val password: String?,
|
|
||||||
val help: Boolean,
|
|
||||||
val loggingLevel: Level,
|
|
||||||
val sshdPort: String?,
|
|
||||||
val sshdHostKeyDirectory: Path?,
|
|
||||||
val trustStorePassword: String?,
|
|
||||||
val trustStoreFile: Path?,
|
|
||||||
val trustStoreType: String?) {
|
|
||||||
|
|
||||||
private fun toConfigFile(): Config {
|
|
||||||
val cmdOpts = mutableMapOf<String, Any?>()
|
|
||||||
|
|
||||||
commandsDirectory?.apply { cmdOpts["extensions.commands.path"] = this.toString() }
|
|
||||||
cordappsDirectory?.apply { cmdOpts["extensions.cordapps.path"] = this.toString() }
|
|
||||||
user?.apply { cmdOpts["node.user"] = this }
|
|
||||||
password?.apply { cmdOpts["node.password"] = this }
|
|
||||||
host?.apply { cmdOpts["node.addresses.rpc.host"] = this }
|
|
||||||
port?.apply { cmdOpts["node.addresses.rpc.port"] = this }
|
|
||||||
trustStoreFile?.apply { cmdOpts["ssl.truststore.path"] = this.toString() }
|
|
||||||
trustStorePassword?.apply { cmdOpts["ssl.truststore.password"] = this }
|
|
||||||
trustStoreType?.apply { cmdOpts["ssl.truststore.type"] = this }
|
|
||||||
sshdPort?.apply {
|
|
||||||
cmdOpts["extensions.sshd.port"] = this
|
|
||||||
cmdOpts["extensions.sshd.enabled"] = true
|
|
||||||
}
|
|
||||||
sshdHostKeyDirectory?.apply { cmdOpts["extensions.sshd.hostkeypath"] = this.toString() }
|
|
||||||
|
|
||||||
return ConfigFactory.parseMap(cmdOpts)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return configuration parsed from an optional config file (provided by the command line option)
|
|
||||||
* and then overridden by the command line options */
|
|
||||||
fun toConfig(): ShellConfiguration {
|
|
||||||
val fileConfig = configFile?.let { ConfigFactory.parseFile(Paths.get(configFile).toFile()) }
|
|
||||||
?: ConfigFactory.empty()
|
|
||||||
val typeSafeConfig = toConfigFile().withFallback(fileConfig).resolve()
|
|
||||||
val shellConfigFile = typeSafeConfig.parseAs<ShellConfigurationFile.ShellConfigFile>()
|
|
||||||
return shellConfigFile.toShellConfiguration()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Object representation of Shell configuration file */
|
|
||||||
private class ShellConfigurationFile {
|
|
||||||
data class Rpc(
|
|
||||||
val host: String,
|
|
||||||
val port: Int)
|
|
||||||
|
|
||||||
data class Addresses(
|
|
||||||
val rpc: Rpc
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Node(
|
|
||||||
val addresses: Addresses,
|
|
||||||
val user: String?,
|
|
||||||
val password: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Cordapps(
|
|
||||||
val path: String
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Sshd(
|
|
||||||
val enabled: Boolean,
|
|
||||||
val port: Int,
|
|
||||||
val hostkeypath: String?
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Commands(
|
|
||||||
val path: String
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Extensions(
|
|
||||||
val cordapps: Cordapps,
|
|
||||||
val sshd: Sshd,
|
|
||||||
val commands: Commands?
|
|
||||||
)
|
|
||||||
|
|
||||||
data class KeyStore(
|
|
||||||
val path: String,
|
|
||||||
val type: String = "JKS",
|
|
||||||
val password: String
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Ssl(
|
|
||||||
val truststore: KeyStore
|
|
||||||
)
|
|
||||||
|
|
||||||
data class ShellConfigFile(
|
|
||||||
val node: Node,
|
|
||||||
val extensions: Extensions?,
|
|
||||||
val ssl: Ssl?
|
|
||||||
) {
|
|
||||||
fun toShellConfiguration(): ShellConfiguration {
|
|
||||||
|
|
||||||
val sslOptions =
|
|
||||||
ssl?.let {
|
|
||||||
ClientRpcSslOptions(
|
|
||||||
trustStorePath = Paths.get(it.truststore.path),
|
|
||||||
trustStorePassword = it.truststore.password)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ShellConfiguration(
|
|
||||||
commandsDirectory = extensions?.commands?.let { Paths.get(it.path) } ?: Paths.get(".")
|
|
||||||
/ COMMANDS_DIR,
|
|
||||||
cordappsDirectory = extensions?.cordapps?.let { Paths.get(it.path) },
|
|
||||||
user = node.user ?: "",
|
|
||||||
password = node.password ?: "",
|
|
||||||
hostAndPort = NetworkHostAndPort(node.addresses.rpc.host, node.addresses.rpc.port),
|
|
||||||
ssl = sslOptions,
|
|
||||||
sshdPort = extensions?.sshd?.let { if (it.enabled) it.port else null },
|
|
||||||
sshHostKeyDirectory = extensions?.sshd?.let { if (it.enabled && it.hostkeypath != null) Paths.get(it.hostkeypath) else null })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,97 +1,44 @@
|
|||||||
package net.corda.tools.shell
|
package net.corda.tools.shell
|
||||||
|
|
||||||
import net.corda.core.internal.toPath
|
import net.corda.core.internal.toPath
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
|
||||||
import net.corda.core.messaging.ClientRpcSslOptions
|
import net.corda.core.messaging.ClientRpcSslOptions
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.slf4j.event.Level
|
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
class StandaloneShellArgsParserTest {
|
class StandaloneShellArgsParserTest {
|
||||||
private val CONFIG_FILE = StandaloneShellArgsParserTest::class.java.getResource("/config.conf").toPath()
|
private val CONFIG_FILE = StandaloneShellArgsParserTest::class.java.getResource("/config.conf").toPath()
|
||||||
|
|
||||||
@Test
|
|
||||||
fun args_to_cmd_options() {
|
|
||||||
val args = arrayOf("--config-file", "/x/y/z/config.conf",
|
|
||||||
"--commands-directory", "/x/y/commands",
|
|
||||||
"--cordpass-directory", "/x/y/cordapps",
|
|
||||||
"--host", "alocalhost",
|
|
||||||
"--port", "1234",
|
|
||||||
"--user", "demo",
|
|
||||||
"--password", "abcd1234",
|
|
||||||
"--logging-level", "DEBUG",
|
|
||||||
"--sshd-port", "2223",
|
|
||||||
"--sshd-hostkey-directory", "/x/y/ssh",
|
|
||||||
"--help",
|
|
||||||
"--truststore-password", "pass2",
|
|
||||||
"--truststore-file", "/x/y/truststore.jks",
|
|
||||||
"--truststore-type", "dummy")
|
|
||||||
|
|
||||||
val expectedOptions = CommandLineOptions(
|
|
||||||
configFile = "/x/y/z/config.conf",
|
|
||||||
commandsDirectory = Paths.get("/x/y/commands").normalize().toAbsolutePath(),
|
|
||||||
cordappsDirectory = Paths.get("/x/y/cordapps").normalize().toAbsolutePath(),
|
|
||||||
host = "alocalhost",
|
|
||||||
port = "1234",
|
|
||||||
user = "demo",
|
|
||||||
password = "abcd1234",
|
|
||||||
help = true,
|
|
||||||
loggingLevel = Level.DEBUG,
|
|
||||||
sshdPort = "2223",
|
|
||||||
sshdHostKeyDirectory = Paths.get("/x/y/ssh").normalize().toAbsolutePath(),
|
|
||||||
trustStorePassword = "pass2",
|
|
||||||
trustStoreFile = Paths.get("/x/y/truststore.jks").normalize().toAbsolutePath(),
|
|
||||||
trustStoreType = "dummy")
|
|
||||||
|
|
||||||
val options = CommandLineOptionParser().parse(*args)
|
|
||||||
|
|
||||||
assertThat(options).isEqualTo(expectedOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun empty_args_to_cmd_options() {
|
fun empty_args_to_cmd_options() {
|
||||||
val args = emptyArray<String>()
|
val expectedOptions = ShellCmdLineOptions()
|
||||||
|
|
||||||
val expectedOptions = CommandLineOptions(configFile = null,
|
assertEquals(expectedOptions.configFile, null)
|
||||||
commandsDirectory = null,
|
assertEquals(expectedOptions.cordappDirectory, null)
|
||||||
cordappsDirectory = null,
|
assertEquals(expectedOptions.commandsDirectory, null)
|
||||||
host = null,
|
assertEquals(expectedOptions.host, null)
|
||||||
port = null,
|
assertEquals(expectedOptions.port, null)
|
||||||
user = null,
|
assertEquals(expectedOptions.user, null)
|
||||||
password = null,
|
assertEquals(expectedOptions.password, null)
|
||||||
help = false,
|
assertEquals(expectedOptions.sshdPort, null)
|
||||||
loggingLevel = Level.INFO,
|
|
||||||
sshdPort = null,
|
|
||||||
sshdHostKeyDirectory = null,
|
|
||||||
trustStorePassword = null,
|
|
||||||
trustStoreFile = null,
|
|
||||||
trustStoreType = null)
|
|
||||||
|
|
||||||
val options = CommandLineOptionParser().parse(*args)
|
|
||||||
|
|
||||||
assertEquals(expectedOptions, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun args_to_config() {
|
fun args_to_config() {
|
||||||
|
val options = ShellCmdLineOptions()
|
||||||
val options = CommandLineOptions(configFile = null,
|
options.configFile = null
|
||||||
commandsDirectory = Paths.get("/x/y/commands"),
|
options.commandsDirectory = Paths.get("/x/y/commands")
|
||||||
cordappsDirectory = Paths.get("/x/y/cordapps"),
|
options.cordappDirectory = Paths.get("/x/y/cordapps")
|
||||||
host = "alocalhost",
|
options.host = "alocalhost"
|
||||||
port = "1234",
|
options.port = "1234"
|
||||||
user = "demo",
|
options.user = "demo"
|
||||||
password = "abcd1234",
|
options.password = "abcd1234"
|
||||||
help = true,
|
options.sshdPort = "2223"
|
||||||
loggingLevel = Level.DEBUG,
|
options.sshdHostKeyDirectory = Paths.get("/x/y/ssh")
|
||||||
sshdPort = "2223",
|
options.trustStorePassword = "pass2"
|
||||||
sshdHostKeyDirectory = Paths.get("/x/y/ssh"),
|
options.trustStoreFile = Paths.get("/x/y/truststore.jks")
|
||||||
trustStorePassword = "pass2",
|
options.trustStoreType = "dummy"
|
||||||
trustStoreFile = Paths.get("/x/y/truststore.jks"),
|
|
||||||
trustStoreType = "dummy"
|
|
||||||
)
|
|
||||||
|
|
||||||
val expectedSsl = ClientRpcSslOptions(
|
val expectedSsl = ClientRpcSslOptions(
|
||||||
trustStorePath = Paths.get("/x/y/truststore.jks"),
|
trustStorePath = Paths.get("/x/y/truststore.jks"),
|
||||||
@ -114,21 +61,19 @@ class StandaloneShellArgsParserTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun cmd_options_to_config_from_file() {
|
fun cmd_options_to_config_from_file() {
|
||||||
|
val options = ShellCmdLineOptions()
|
||||||
val options = CommandLineOptions(configFile = CONFIG_FILE.toString(),
|
options.configFile = CONFIG_FILE
|
||||||
commandsDirectory = null,
|
options.commandsDirectory = null
|
||||||
cordappsDirectory = null,
|
options.cordappDirectory = null
|
||||||
host = null,
|
options.host = null
|
||||||
port = null,
|
options.port = null
|
||||||
user = null,
|
options.user = null
|
||||||
password = null,
|
options.password = null
|
||||||
help = false,
|
options.sshdPort = null
|
||||||
loggingLevel = Level.DEBUG,
|
options.sshdHostKeyDirectory = null
|
||||||
sshdPort = null,
|
options.trustStorePassword = null
|
||||||
sshdHostKeyDirectory = null,
|
options.trustStoreFile = null
|
||||||
trustStorePassword = null,
|
options.trustStoreType = null
|
||||||
trustStoreFile = null,
|
|
||||||
trustStoreType = null)
|
|
||||||
|
|
||||||
val expectedConfig = ShellConfiguration(
|
val expectedConfig = ShellConfiguration(
|
||||||
commandsDirectory = Paths.get("/x/y/commands"),
|
commandsDirectory = Paths.get("/x/y/commands"),
|
||||||
@ -148,21 +93,18 @@ class StandaloneShellArgsParserTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun cmd_options_override_config_from_file() {
|
fun cmd_options_override_config_from_file() {
|
||||||
|
val options = ShellCmdLineOptions()
|
||||||
val options = CommandLineOptions(configFile = CONFIG_FILE.toString(),
|
options.configFile = CONFIG_FILE
|
||||||
commandsDirectory = null,
|
options.commandsDirectory = null
|
||||||
cordappsDirectory = null,
|
options.host = null
|
||||||
host = null,
|
options.port = null
|
||||||
port = null,
|
options.user = null
|
||||||
user = null,
|
options.password = "blabla"
|
||||||
password = "blabla",
|
options.sshdPort = null
|
||||||
help = false,
|
options.sshdHostKeyDirectory = null
|
||||||
loggingLevel = Level.DEBUG,
|
options.trustStorePassword = null
|
||||||
sshdPort = null,
|
options.trustStoreFile = null
|
||||||
sshdHostKeyDirectory = null,
|
options.trustStoreType = null
|
||||||
trustStorePassword = null,
|
|
||||||
trustStoreFile = null,
|
|
||||||
trustStoreType = null)
|
|
||||||
|
|
||||||
val expectedSsl = ClientRpcSslOptions(
|
val expectedSsl = ClientRpcSslOptions(
|
||||||
trustStorePath = Paths.get("/x/y/truststore.jks"),
|
trustStorePath = Paths.get("/x/y/truststore.jks"),
|
||||||
|
Reference in New Issue
Block a user