[CORDA-2055] [CORDA-2236]: Bootstrapper cordapp copying (#4309)

This commit is contained in:
Anthony Keenan 2018-11-28 13:28:56 +00:00 committed by Michele Sollecito
parent 9d748e7b0c
commit 994afcfef7
6 changed files with 111 additions and 59 deletions

View File

@ -82,7 +82,7 @@ Whitelisting contracts
Any CorDapps provided when bootstrapping a network will be scanned for contracts which will be used to create the
*Zone whitelist* (see :doc:`api-contract-constraints`) for the network.
.. note:: If you only wish to whitelist the CorDapps but not copy them to each node then run with the ``--no-copy`` flag.
.. note:: If you only wish to whitelist the CorDapps but not copy them to each node then run with the ``--copy-cordapps=No`` option.
The CorDapp JARs will be hashed and scanned for ``Contract`` classes. These contract class implementations will become part
of the whitelisted contracts in the network parameters (see ``NetworkParameters.whitelistedContractImplementations`` :doc:`network-map`).
@ -401,16 +401,17 @@ The Network Bootstrapper can be started with the following command line options:
.. code-block:: shell
bootstrapper [-hvV] [--no-copy] [--dir=<dir>] [--event-horizon=<eventHorizon>]
[--logging-level=<loggingLevel>]
bootstrapper [-hvV] [--copy-cordapps=<copyCordapps>] [--dir=<dir>]
[--event-horizon=<eventHorizon>] [--logging-level=<loggingLevel>]
[--max-message-size=<maxMessageSize>]
[--max-transaction-size=<maxTransactionSize>]
[--minimum-platform-version=<minimumPlatformVersion>]
[-n=<networkParametersFile>] [COMMAND]
* ``--dir=<dir>``: Root directory containing the node configuration files and CorDapp JARs that will form the test network.
It may also contain existing node directories. Defaults to the current directory.
* ``--no-copy``: Don't copy the CorDapp JARs into the nodes' "cordapps" directories.
It may also contain existing node directories. Defaults to the current directory.
* ``--copy-cordapps=<copyCordapps>``: Whether or not to copy the CorDapp JARs into the nodes' 'cordapps' directory. Possible values:
FirstRunOnly, Yes, No. Default: FirstRunOnly.
* ``--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.
* ``--help``, ``-h``: Show this help message and exit.
@ -419,8 +420,8 @@ The Network Bootstrapper can be started with the following command line options:
* ``--max-message-size``: The maximum message size to use in the network-parameters, in bytes.
* ``--max-transaction-size``: The maximum transaction size to use in the network-parameters, in bytes.
* ``--event-horizon``: The event horizon to use in the network-parameters.
* ``--network-parameter-overrides=<networkParametersFile>``, ``-n=<networkParametersFile>`: Overrides the default network parameters with those
in the given file. See `Overriding network parameters via a file`_ for more information.
* ``--network-parameter-overrides=<networkParametersFile>``, ``-n=<networkParametersFile>``: Overrides the default network parameters with those
in the given file. See `Overriding network parameters via a file`_ for more information.
Sub-commands

View File

@ -75,6 +75,8 @@ internal constructor(private val initSerEnv: Boolean,
private const val LOGS_DIR_NAME = "logs"
private val jarsThatArentCordapps = setOf("corda.jar", "runnodes.jar")
private fun extractEmbeddedCordaJar(): InputStream {
return Thread.currentThread().contextClassLoader.getResourceAsStream("corda.jar")
}
@ -182,21 +184,21 @@ internal constructor(private val initSerEnv: Boolean,
* TODO: Remove once the gradle plugins are updated to 4.0.30
*/
fun bootstrap(directory: Path, cordappJars: List<Path>) {
bootstrap(directory, cordappJars, copyCordapps = true, fromCordform = true)
bootstrap(directory, cordappJars, CopyCordapps.Yes, fromCordform = true)
}
/** Entry point for Cordform */
fun bootstrapCordform(directory: Path, cordappJars: List<Path>) {
bootstrap(directory, cordappJars, copyCordapps = false, fromCordform = true)
bootstrap(directory, cordappJars, CopyCordapps.No, fromCordform = true)
}
/** Entry point for the tool */
override fun bootstrap(directory: Path, copyCordapps: Boolean, networkParameterOverrides: NetworkParametersOverrides) {
override fun bootstrap(directory: Path, copyCordapps: CopyCordapps, networkParameterOverrides: NetworkParametersOverrides) {
require(networkParameterOverrides.minimumPlatformVersion == null || networkParameterOverrides.minimumPlatformVersion <= PLATFORM_VERSION) { "Minimum platform version cannot be greater than $PLATFORM_VERSION" }
// Don't accidentally include the bootstrapper jar as a CorDapp!
val bootstrapperJar = javaClass.location.toPath()
val cordappJars = directory.list { paths ->
paths.filter { it.toString().endsWith(".jar") && !it.isSameAs(bootstrapperJar) && it.fileName.toString() != "corda.jar" }
paths.filter { it.toString().endsWith(".jar") && !it.isSameAs(bootstrapperJar) && !jarsThatArentCordapps.contains(it.fileName.toString().toLowerCase()) }
.toList()
}
bootstrap(directory, cordappJars, copyCordapps, fromCordform = false, networkParametersOverrides = networkParameterOverrides)
@ -205,16 +207,13 @@ internal constructor(private val initSerEnv: Boolean,
private fun bootstrap(
directory: Path,
cordappJars: List<Path>,
copyCordapps: Boolean,
copyCordapps: CopyCordapps,
fromCordform: Boolean,
networkParametersOverrides: NetworkParametersOverrides = NetworkParametersOverrides()
) {
directory.createDirectories()
println("Bootstrapping local test network in $directory")
if (!fromCordform) {
println("Found the following CorDapps: ${cordappJars.map { it.fileName }}")
}
createNodeDirectoriesIfNeeded(directory, fromCordform)
val networkAlreadyExists = createNodeDirectoriesIfNeeded(directory, fromCordform)
val nodeDirs = gatherNodeDirectories(directory)
require(nodeDirs.isNotEmpty()) { "No nodes found" }
@ -224,19 +223,9 @@ internal constructor(private val initSerEnv: Boolean,
val configs = nodeDirs.associateBy({ it }, { ConfigFactory.parseFile((it / "node.conf").toFile()) })
checkForDuplicateLegalNames(configs.values)
if (copyCordapps && cordappJars.isNotEmpty()) {
println("Copying CorDapp JARs into node directories")
for (nodeDir in nodeDirs) {
val cordappsDir = (nodeDir / "cordapps").createDirectories()
cordappJars.forEach {
try {
it.copyToDirectory(cordappsDir)
} catch (e: FileAlreadyExistsException) {
println("WARNING: ${it.fileName} already exists in $cordappsDir, ignoring and leaving existing CorDapp untouched")
}
}
}
}
copyCordapps.copy(cordappJars, nodeDirs, networkAlreadyExists, fromCordform)
generateServiceIdentitiesForNotaryClusters(configs)
if (initSerEnv) {
initialiseSerialization()
@ -268,7 +257,8 @@ internal constructor(private val initSerEnv: Boolean,
}
}
private fun createNodeDirectoriesIfNeeded(directory: Path, fromCordform: Boolean) {
private fun createNodeDirectoriesIfNeeded(directory: Path, fromCordform: Boolean): Boolean {
var networkAlreadyExists = false
val cordaJar = directory / "corda.jar"
var usingEmbedded = false
if (!cordaJar.exists()) {
@ -284,6 +274,10 @@ internal constructor(private val initSerEnv: Boolean,
for (confFile in confFiles) {
val nodeName = confFile.fileName.toString().removeSuffix("_node.conf")
println("Generating node directory for $nodeName")
if ((directory / nodeName).exists()) {
//directory already exists, so assume this network has been bootstrapped before
networkAlreadyExists = true
}
val nodeDir = (directory / nodeName).createDirectories()
confFile.copyTo(nodeDir / "node.conf", REPLACE_EXISTING)
webServerConfFiles.firstOrNull { directory.relativize(it).toString().removeSuffix("_web-server.conf") == nodeName }
@ -306,6 +300,7 @@ internal constructor(private val initSerEnv: Boolean,
if (fromCordform || usingEmbedded) {
cordaJar.delete()
}
return networkAlreadyExists
}
private fun gatherNodeDirectories(directory: Path): List<Path> {
@ -467,7 +462,8 @@ fun NetworkParameters.overrideWith(override: NetworkParametersOverrides): Networ
maxMessageSize = override.maxMessageSize ?: this.maxMessageSize,
maxTransactionSize = override.maxTransactionSize ?: this.maxTransactionSize,
eventHorizon = override.eventHorizon ?: this.eventHorizon,
packageOwnership = override.packageOwnership?.map { it.javaPackageName to it.publicKey }?.toMap() ?: this.packageOwnership
packageOwnership = override.packageOwnership?.map { it.javaPackageName to it.publicKey }?.toMap()
?: this.packageOwnership
)
}
@ -482,5 +478,50 @@ data class NetworkParametersOverrides(
)
interface NetworkBootstrapperWithOverridableParameters {
fun bootstrap(directory: Path, copyCordapps: Boolean, networkParameterOverrides: NetworkParametersOverrides = NetworkParametersOverrides())
fun bootstrap(directory: Path, copyCordapps: CopyCordapps, networkParameterOverrides: NetworkParametersOverrides = NetworkParametersOverrides())
}
enum class CopyCordapps {
FirstRunOnly {
override fun copy(cordappJars: List<Path>, nodeDirs: List<Path>, networkAlreadyExists: Boolean) {
if (networkAlreadyExists) {
println("Not copying CorDapp JARs as --copy-cordapps is set to FirstRunOnly, and it looks like this network has already been bootstrapped.")
return
}
cordappJars.copy(nodeDirs)
}
},
Yes {
override fun copy(cordappJars: List<Path>, nodeDirs: List<Path>, networkAlreadyExists: Boolean) = cordappJars.copy(nodeDirs)
},
No {
override fun copy(cordappJars: List<Path>, nodeDirs: List<Path>, networkAlreadyExists: Boolean) = println("Not copying CorDapp JARs as --copy-cordapps is set to No.")
};
protected abstract fun copy(cordappJars: List<Path>, nodeDirs: List<Path>, networkAlreadyExists: Boolean)
protected fun List<Path>.copy(nodeDirs: List<Path>) {
if (this.isNotEmpty()) {
println("Copying CorDapp JARs into node directories")
for (nodeDir in nodeDirs) {
val cordappsDir = (nodeDir / "cordapps").createDirectories()
this.forEach {
try {
it.copyToDirectory(cordappsDir)
} catch (e: FileAlreadyExistsException) {
println("WARNING: ${it.fileName} already exists in $cordappsDir, ignoring and leaving existing CorDapp untouched")
}
}
}
}
}
fun copy(cordappJars: List<Path>, nodeDirs: List<Path>, networkAlreadyExists: Boolean, fromCordform: Boolean) {
if (!fromCordform) {
println("Found the following CorDapps: ${cordappJars.map { it.fileName }}")
}
this.copy(cordappJars, nodeDirs, networkAlreadyExists)
}
}

View File

@ -184,7 +184,7 @@ class NetworkBootstrapperTest {
fun `no copy CorDapps`() {
createNodeConfFile("alice", aliceConfig)
val cordappBytes = createFakeCordappJar("sample-app", listOf("contract.class"))
bootstrap(copyCordapps = false)
bootstrap(copyCordapps = CopyCordapps.No)
val networkParameters = assertBootstrappedNetwork(fakeEmbeddedCordaJar, "alice" to aliceConfig)
assertThat(rootDir / "alice" / "cordapps" / "sample-app.jar").doesNotExist()
assertThat(networkParameters.whitelistedContractImplementations).isEqualTo(mapOf(
@ -305,7 +305,7 @@ class NetworkBootstrapperTest {
return bytes
}
private fun bootstrap(copyCordapps: Boolean = true,
private fun bootstrap(copyCordapps: CopyCordapps = CopyCordapps.FirstRunOnly,
packageOwnership: Map<String, PublicKey>? = emptyMap(),
minimumPlatformVerison: Int? = PLATFORM_VERSION,
maxMessageSize: Int? = DEFAULT_MAX_MESSAGE_SIZE,

View File

@ -2,18 +2,13 @@ package net.corda.bootstrapper
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import net.corda.cliutils.CordaCliWrapper
import net.corda.cliutils.ExitCodes
import net.corda.cliutils.printError
import net.corda.cliutils.start
import net.corda.cliutils.*
import net.corda.common.configuration.parsing.internal.Configuration
import net.corda.core.internal.PLATFORM_VERSION
import net.corda.core.internal.exists
import net.corda.nodeapi.internal.network.NetworkBootstrapper
import net.corda.nodeapi.internal.network.*
import net.corda.nodeapi.internal.network.NetworkBootstrapper.Companion.DEFAULT_MAX_MESSAGE_SIZE
import net.corda.nodeapi.internal.network.NetworkBootstrapper.Companion.DEFAULT_MAX_TRANSACTION_SIZE
import net.corda.nodeapi.internal.network.NetworkBootstrapperWithOverridableParameters
import net.corda.nodeapi.internal.network.NetworkParametersOverrides
import picocli.CommandLine.Option
import java.io.FileNotFoundException
import java.nio.file.Path
@ -30,8 +25,11 @@ class NetworkBootstrapperRunner(private val bootstrapper: NetworkBootstrapperWit
"It may also contain existing node directories."])
var dir: Path = Paths.get(".")
@Option(names = ["--no-copy"], description = ["""Don't copy the CorDapp JARs into the nodes' "cordapps" directories."""])
var noCopy: Boolean = false
@Option(names = ["--no-copy"], hidden = true, description = ["""DEPRECATED. Don't copy the CorDapp JARs into the nodes' "cordapps" directories."""])
var noCopy: Boolean? = null
@Option(names = ["--copy-cordapps"], description = ["Whether or not to copy the CorDapp JARs into the nodes' 'cordapps' directory. \${COMPLETION-CANDIDATES}"])
var copyCordapps: CopyCordapps = CopyCordapps.FirstRunOnly
@Option(names = ["--minimum-platform-version"], description = ["The minimum platform version to use in the network-parameters. Current default is $PLATFORM_VERSION."])
var minimumPlatformVersion: Int? = null
@ -85,10 +83,14 @@ class NetworkBootstrapperRunner(private val bootstrapper: NetworkBootstrapperWit
}
override fun runProgram(): Int {
if (noCopy != null) {
printWarning("The --no-copy parameter has been deprecated and been replaced with the --copy-cordapps parameter.")
copyCordapps = if (noCopy == true) CopyCordapps.No else CopyCordapps.Yes
}
verifyInputs()
val networkParameterOverrides = getNetworkParametersOverrides().doOnErrors(::reportErrors).optional ?: return ExitCodes.FAILURE
bootstrapper.bootstrap(dir.toAbsolutePath().normalize(),
copyCordapps = !noCopy,
copyCordapps = copyCordapps,
networkParameterOverrides = networkParameterOverrides
)
return ExitCodes.SUCCESS

View File

@ -6,12 +6,11 @@ import net.corda.core.internal.copyTo
import net.corda.core.internal.deleteRecursively
import net.corda.core.internal.div
import net.corda.core.utilities.days
import net.corda.nodeapi.internal.network.CopyCordapps
import net.corda.nodeapi.internal.network.NetworkBootstrapperWithOverridableParameters
import net.corda.nodeapi.internal.network.NetworkParametersOverrides
import net.corda.nodeapi.internal.network.PackageOwner
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.JarSignatureTestUtils.generateKey
import net.corda.testing.core.JarSignatureTestUtils.getPublicKey
import org.junit.*
@ -92,7 +91,7 @@ class NetworkBootstrapperRunnerTests {
fun `test when defaults are run bootstrapper is called correctly`() {
val (runner, mockBootstrapper) = getRunner()
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), true, NetworkParametersOverrides())
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides())
assertEquals(0, exitCode)
}
@ -102,16 +101,25 @@ class NetworkBootstrapperRunnerTests {
val tempDir = createTempDir()
runner.dir = tempDir.toPath()
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(tempDir.toPath().toAbsolutePath().normalize(), true, NetworkParametersOverrides())
verify(mockBootstrapper).bootstrap(tempDir.toPath().toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides())
assertEquals(0, exitCode)
}
@Test
fun `test when no copy flag is specified it is passed through to the bootstrapper`() {
val (runner, mockBootstrapper) = getRunner()
runner.noCopy = true
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.No, NetworkParametersOverrides())
assertEquals(0, exitCode)
}
@Test
fun `test when copy cordapps is specified it is passed through to the bootstrapper`() {
val (runner, mockBootstrapper) = getRunner()
runner.noCopy = true
runner.copyCordapps = CopyCordapps.No
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), false, NetworkParametersOverrides())
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.No, NetworkParametersOverrides())
assertEquals(0, exitCode)
}
@ -120,7 +128,7 @@ class NetworkBootstrapperRunnerTests {
val (runner, mockBootstrapper) = getRunner()
runner.minimumPlatformVersion = 1
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), true, NetworkParametersOverrides(minimumPlatformVersion = 1))
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides(minimumPlatformVersion = 1))
assertEquals(0, exitCode)
}
@ -137,7 +145,7 @@ class NetworkBootstrapperRunnerTests {
val (runner, mockBootstrapper) = getRunner()
runner.maxMessageSize = 1
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), true, NetworkParametersOverrides(maxMessageSize = 1))
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides(maxMessageSize = 1))
assertEquals(0, exitCode)
}
@ -154,7 +162,7 @@ class NetworkBootstrapperRunnerTests {
val (runner, mockBootstrapper) = getRunner()
runner.maxTransactionSize = 1
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), true, NetworkParametersOverrides(maxTransactionSize = 1))
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides(maxTransactionSize = 1))
assertEquals(0, exitCode)
}
@ -171,7 +179,7 @@ class NetworkBootstrapperRunnerTests {
val (runner, mockBootstrapper) = getRunner()
runner.eventHorizon = 7.days
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), true, NetworkParametersOverrides(eventHorizon = 7.days))
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides(eventHorizon = 7.days))
assertEquals(0, exitCode)
}
@ -189,7 +197,7 @@ class NetworkBootstrapperRunnerTests {
val conf = correctNetworkFile.copyToTestDir()
runner.networkParametersFile = conf
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), true, NetworkParametersOverrides(
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides(
maxMessageSize = 10000,
maxTransactionSize = 2000,
eventHorizon = 5.days,
@ -204,7 +212,7 @@ class NetworkBootstrapperRunnerTests {
val conf = aliceConfigFile.copyToTestDir()
runner.networkParametersFile = conf
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), true, NetworkParametersOverrides(
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides(
packageOwnership = listOf(PackageOwner("com.example.stuff", publicKey = alicePublicKey))
))
assertEquals(0, exitCode)
@ -216,7 +224,7 @@ class NetworkBootstrapperRunnerTests {
val conf = aliceConfigFile.copyToTestDir(dirAliceEC)
runner.networkParametersFile = conf
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), true, NetworkParametersOverrides(
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides(
packageOwnership = listOf(PackageOwner("com.example.stuff", publicKey = alicePublicKeyEC))
))
assertEquals(0, exitCode)
@ -228,7 +236,7 @@ class NetworkBootstrapperRunnerTests {
val conf = aliceConfigFile.copyToTestDir(dirAliceDSA)
runner.networkParametersFile = conf
val exitCode = runner.runProgram()
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), true, NetworkParametersOverrides(
verify(mockBootstrapper).bootstrap(Paths.get(".").toAbsolutePath().normalize(), CopyCordapps.FirstRunOnly, NetworkParametersOverrides(
packageOwnership = listOf(PackageOwner("com.example.stuff", publicKey = alicePublicKeyDSA))
))
assertEquals(0, exitCode)

View File

@ -22,7 +22,7 @@
- "DEBUG"
- "TRACE"
- parameterName: "--no-copy"
parameterType: "boolean"
parameterType: "java.lang.Boolean"
required: false
multiParam: false
acceptableValues: []