Corda Behave framework updates to decouple from Network Services (#914)

* Added new 'eventHorizonDays' configuration item.

* Added custom authenticator to provide ARTIFACTORY credentials upon resolving protected internal URL.

* Re-pointed functional test scenarios to point to Enterprise master.

* Enterprise corda network scenarios to use NetworkBootstrapper tool (instead of Doorman/NMS) since decision made by Product Management to split out Doorman/NMS from Enterprise.

* Configuration parameter changed to 'runMigration' since DP3.

* Fixed problem with logger upon startup.

* General improvements and additions to setup and execution scripts (eg. SIMM valuation demo now consists of 3 separate jars)

* Updates to decouple from Network Services.

* Download all JARs from artifactory for a given published distribution.

* Add TODOs for Doorman/NMS rework.

* Addressing TL PR review comments.

* Additional instructions for setting up the environment and running QA tests.

* Fixed repository URL references.
This commit is contained in:
josecoll
2018-06-05 15:02:07 +01:00
committed by GitHub
parent 4de4e8a3ef
commit c2f22e18a5
18 changed files with 435 additions and 80 deletions

View File

@ -56,3 +56,34 @@ $ ../../gradlew scenario -Ptags="@cash"
# or # or
$ ../../gradlew scenario -Ptags="@cash,@logging" $ ../../gradlew scenario -Ptags="@cash,@logging"
``` ```
# Environment variables and system properties
The following environment variables must be set to enable access to the internal R3 Artifactory repository hosting Enterprise distributions:
https://ci-artifactory.corda.r3cev.com/artifactory/r3-corda-releases
```bash
CORDA_ARTIFACTORY_USERNAME
CORDA_ARTIFACTORY_PASSWORD
```
The following system properties may be passed to the Cucumber and ScenarioRunner run-time processes:
* `STAGING_ROOT` to specify the filesystem location of the Corda distributions to be used (as setup by the prepare.sh script)
```bash
e.g. -DSTAGING_ROOT=$HOME/staging
```
* `USE_NETWORK_SERVICES` specifies to use the Doorman/NMS service to perform setup of an Enterprise network.
By default both OS and Enterprise scenarios will use the [Network Bootstrapper utility](https://docs.corda.net/head/setting-up-a-corda-network.html#bootstrapping-the-network) to create a Corda network.
```bash
e.g. -DUSE_NETWORK_SERVICES
```
* `DISABLE_CLEANUP` to prevent clean-up of runtime directories after successfully running tests.
```bash
e.g. -DDISABLE_CLEANUP
```

View File

@ -7,13 +7,21 @@ set -x
# corda-master => git clone https://github.com/corda/corda # corda-master => git clone https://github.com/corda/corda
# r3corda-master => git clone https://github.com/corda/enterprise # r3corda-master => git clone https://github.com/corda/enterprise
VERSION=r3corda-master VERSION=r3corda-master
STAGING_DIR=~/staging
STAGING_DIR="${STAGING_ROOT:-$TMPDIR/staging}"
echo "Staging directory: $STAGING_DIR"
CORDA_DIR=${STAGING_DIR}/corda/${VERSION} CORDA_DIR=${STAGING_DIR}/corda/${VERSION}
echo "Corda staging directory: $CORDA_DIR"
CORDAPP_DIR=${CORDA_DIR}/apps CORDAPP_DIR=${CORDA_DIR}/apps
echo "CorDapp staging directory: $CORDAPP_DIR"
DRIVERS_DIR=${STAGING_DIR}/drivers DRIVERS_DIR=${STAGING_DIR}/drivers
echo "Drivers staging directory: $DRIVERS_DIR"
# Set up directories # Set up directories
mkdir -p ${STAGING_DIR} mkdir -p ${STAGING_DIR} || { echo "Unable to create directory $STAGING_DIR"; exit; }
mkdir -p ${CORDA_DIR} mkdir -p ${CORDA_DIR}
mkdir -p ${CORDAPP_DIR} mkdir -p ${CORDAPP_DIR}
mkdir -p ${DRIVERS_DIR} mkdir -p ${DRIVERS_DIR}
@ -27,8 +35,14 @@ cp -v $(ls node/capsule/build/libs/corda-*.jar | tail -n1) ${CORDA_DIR}/corda.ja
cp -v $(ls finance/build/libs/corda-finance-*.jar | tail -n1) ${CORDAPP_DIR} cp -v $(ls finance/build/libs/corda-finance-*.jar | tail -n1) ${CORDAPP_DIR}
# Copy sample Cordapps # Copy sample Cordapps
# SIMM valuation demo
./gradlew samples:simm-valuation-demo:jar ./gradlew samples:simm-valuation-demo:jar
cp -v $(ls samples/simm-valuation-demo/build/libs/simm-valuation-demo-*.jar | tail -n1) ${CORDAPP_DIR} cp -v $(ls samples/simm-valuation-demo/build/libs/simm-valuation-demo-*.jar | tail -n1) ${CORDAPP_DIR}
./gradlew samples:simm-valuation-demo:contracts-states:jar
cp -v $(ls samples/simm-valuation-demo/contracts-states/build/libs/contracts-states-*.jar | tail -n1) ${CORDAPP_DIR}
./gradlew samples:simm-valuation-demo:flows:jar
cp -v $(ls samples/simm-valuation-demo/flows/build/libs/flows-*.jar | tail -n1) ${CORDAPP_DIR}
# Download database drivers # Download database drivers
curl "https://search.maven.org/remotecontent?filepath=com/h2database/h2/1.4.196/h2-1.4.196.jar" > ${DRIVERS_DIR}/h2-1.4.196.jar curl "https://search.maven.org/remotecontent?filepath=com/h2database/h2/1.4.196/h2-1.4.196.jar" > ${DRIVERS_DIR}/h2-1.4.196.jar
@ -39,11 +53,17 @@ curl -L "https://github.com/Microsoft/mssql-jdbc/releases/download/v6.2.2/mssql-
./gradlew buildBootstrapperJar ./gradlew buildBootstrapperJar
cp -v $(ls tools/bootstrapper/build/libs/*.jar | tail -n1) ${CORDA_DIR}/network-bootstrapper.jar cp -v $(ls tools/bootstrapper/build/libs/*.jar | tail -n1) ${CORDA_DIR}/network-bootstrapper.jar
# build and distribute Doorman/NMS # TODO: resolve Doorman/NMS artifacts from new artifactory location.
./gradlew network-management:capsule:buildDoormanJAR # Doorman/NMS has moved to a separate repo: https://github.com/corda/network-services,
cp -v $(ls network-management/capsule/build/libs/doorman-*.jar | tail -n1) ${CORDA_DIR}/doorman.jar # now follows a different release cycle (and versioning scheme), and is published to a new artifactory location:
# https://ci-artifactory.corda.r3cev.com/artifactory/r3-corda-releases/com/r3/corda/networkservices/doorman/
# build and distribute DB Migration tool # build and distribute DB Migration tool
./gradlew tools:dbmigration:shadowJar ./gradlew tools:dbmigration:shadowJar
cp -v $(ls tools/dbmigration/build/libs/*migration-*.jar | tail -n1) ${CORDA_DIR}/dbmigration.jar cp -v $(ls tools/dbmigration/build/libs/*migration-*.jar | tail -n1) ${CORDA_DIR}/dbmigration.jar
# Build rpcProxy (required by CTS Scenario Driver to call Corda 3.0 which continues to use Kryo for RPC)
./gradlew testing:qa:behave:tools:rpc-proxy:rpcProxyJar
cp -v $(ls testing/qa/behave/tools/rpc-proxy/build/libs/corda-rpcProxy*.jar | tail -n1) ${CORDA_DIR}/corda-rpcProxy.jar
cp -v testing/qa/behave/tools/rpc-proxy/startRPCproxy.sh ${CORDA_DIR}

View File

@ -28,7 +28,7 @@ class SqlServerConfigurationTemplate : DatabaseConfigurationTemplate() {
| dataSource.password = "${it.password}" | dataSource.password = "${it.password}"
|} |}
|database = { |database = {
| initialiseSchema=true | runMigration=true
| transactionIsolationLevel = READ_COMMITTED | transactionIsolationLevel = READ_COMMITTED
| schema="${it.schema}" | schema="${it.schema}"
|} |}

View File

@ -108,10 +108,11 @@ class Network private constructor(
throw CordaException("Unable to configure nodes in Corda network. Please check logs in $directory") throw CordaException("Unable to configure nodes in Corda network. Please check logs in $directory")
} }
if (networkType == Distribution.Type.CORDA_ENTERPRISE) if (networkType == Distribution.Type.CORDA_ENTERPRISE && System.getProperty("USE_NETWORK_SERVICES") != null)
// TODO: rework how we use the Doorman/NMS (now these are a separate product / distribution)
network.bootstrapDoorman() network.bootstrapDoorman()
else else
network.bootstrapLocalNetwork() network.bootstrapLocalNetwork(networkType)
return network return network
} }
} }
@ -147,6 +148,10 @@ class Network private constructor(
*/ */
private fun bootstrapDoorman() { private fun bootstrapDoorman() {
// TODO: rework how we use the Doorman/NMS (now these are a separate product / distribution)
signalFailure("Bootstrapping a Corda Enterprise network using the Doorman is no longer supported; exiting ...")
return
// WARNING!! Need to use the correct bootstrapper // WARNING!! Need to use the correct bootstrapper
// only if using OS nodes (need to choose the latest version) // only if using OS nodes (need to choose the latest version)
val r3node = nodes.values val r3node = nodes.values
@ -158,21 +163,25 @@ class Network private constructor(
val doormanTargetDirectory = targetDirectory / "doorman" val doormanTargetDirectory = targetDirectory / "doorman"
source.toFile().copyRecursively(doormanTargetDirectory.toFile(), true) source.toFile().copyRecursively(doormanTargetDirectory.toFile(), true)
// Use master version of Bootstrapper
val doormanJar = Distribution.R3_MASTER.doormanJar
log.info("DoormanJar URL: $doormanJar\n")
// 1. Create key stores for local signer // 1. Create key stores for local signer
// java -jar doorman-<version>.jar --mode ROOT_KEYGEN // java -jar doorman-<version>.jar --mode ROOT_KEYGEN
log.info("Doorman target directory: $doormanTargetDirectory") log.info("Doorman target directory: $doormanTargetDirectory")
runCommand(JarCommand(distribution.doormanJar, runCommand(JarCommand(doormanJar,
arrayOf("--config-file", "$doormanConfigDirectory/node-init.conf", "--mode", "ROOT_KEYGEN", "--trust-store-password", "password"), arrayOf("--config-file", "$doormanConfigDirectory/node-init.conf", "--mode", "ROOT_KEYGEN", "--trust-store-password", "password"),
doormanTargetDirectory, timeout)) doormanTargetDirectory, timeout))
// java -jar doorman-<version>.jar --mode CA_KEYGEN // java -jar doorman-<version>.jar --mode CA_KEYGEN
runCommand(JarCommand(distribution.doormanJar, runCommand(JarCommand(doormanJar,
arrayOf("--config-file", "$doormanConfigDirectory/node-init.conf", "--mode", "CA_KEYGEN"), arrayOf("--config-file", "$doormanConfigDirectory/node-init.conf", "--mode", "CA_KEYGEN"),
doormanTargetDirectory, timeout)) doormanTargetDirectory, timeout))
// 2. Start the doorman service for notary registration // 2. Start the doorman service for notary registration
doormanNMS = JarCommand(distribution.doormanJar, doormanNMS = JarCommand(doormanJar,
arrayOf("--config-file", "$doormanConfigDirectory/node-init.conf"), arrayOf("--config-file", "$doormanConfigDirectory/node-init.conf"),
doormanTargetDirectory, timeout) doormanTargetDirectory, timeout)
@ -216,13 +225,13 @@ class Network private constructor(
// 6. Load initial network parameters file for network map service // 6. Load initial network parameters file for network map service
val networkParamsConfig = if (notaryNodes.isEmpty()) "network-parameters-without-notary.conf" else "network-parameters.conf" val networkParamsConfig = if (notaryNodes.isEmpty()) "network-parameters-without-notary.conf" else "network-parameters.conf"
val updateNetworkParams = JarCommand(distribution.doormanJar, val updateNetworkParams = JarCommand(doormanJar,
arrayOf("--config-file", "$doormanTargetDirectory/node.conf", "--set-network-parameters", "$doormanTargetDirectory/$networkParamsConfig"), arrayOf("--config-file", "$doormanTargetDirectory/node.conf", "--set-network-parameters", "$doormanTargetDirectory/$networkParamsConfig"),
doormanTargetDirectory, timeout) doormanTargetDirectory, timeout)
runCommand(updateNetworkParams) runCommand(updateNetworkParams)
// 7. Start a fully configured Doorman / NMS // 7. Start a fully configured Doorman / NMS
doormanNMS = JarCommand(distribution.doormanJar, doormanNMS = JarCommand(doormanJar,
arrayOf("--config-file", "$doormanConfigDirectory/node.conf"), arrayOf("--config-file", "$doormanConfigDirectory/node.conf"),
doormanTargetDirectory, timeout) doormanTargetDirectory, timeout)
@ -283,12 +292,14 @@ class Network private constructor(
return command return command
} }
private fun bootstrapLocalNetwork() { private fun bootstrapLocalNetwork(networkType: Distribution.Type) {
val bootstrapper = nodes.values // Use master version of Bootstrapper
.filter { it.config.distribution.type != Distribution.Type.CORDA_ENTERPRISE } val bootstrapper =
.sortedByDescending { it.config.distribution.version } when (networkType) {
.first() Distribution.Type.CORDA_OS -> Distribution.MASTER.networkBootstrapper
.config.distribution.networkBootstrapper Distribution.Type.CORDA_ENTERPRISE -> Distribution.R3_MASTER.networkBootstrapper
}
log.info("Bootstrapper URL: $bootstrapper\n")
if (!bootstrapper.exists()) { if (!bootstrapper.exists()) {
signalFailure("Network bootstrapping tool does not exist; exiting ...") signalFailure("Network bootstrapping tool does not exist; exiting ...")
@ -320,7 +331,7 @@ class Network private constructor(
runCommand(rpcProxyCommand) runCommand(rpcProxyCommand)
} }
else { else {
log.warn("Missing RPC proxy startup script. Continuing ...") log.warn("Missing RPC proxy startup script ($startProxyScript). Continuing ...")
} }
} }

View File

@ -17,8 +17,13 @@ import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.exists import net.corda.core.internal.exists
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import java.io.IOException
import java.net.Authenticator
import java.net.PasswordAuthentication
import java.net.URL import java.net.URL
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardCopyOption
/** /**
* Corda distribution. * Corda distribution.
@ -41,9 +46,9 @@ class Distribution private constructor(
file: Path? = null, file: Path? = null,
/** /**
* The URL of the distribution fat JAR, if available. * Map of all distribution JAR artifacts from artifactory, if available.
*/ */
val url: URL? = null, val artifactUrlMap: Map<String,URL>? = null,
/** /**
* The Docker image details, if available * The Docker image details, if available
@ -86,10 +91,22 @@ class Distribution private constructor(
*/ */
fun ensureAvailable() { fun ensureAvailable() {
if (cordaJar.exists()) return if (cordaJar.exists()) return
val url = checkNotNull(url) { "File not found $cordaJar" }
try { try {
cordaJar.parent.createDirectories() path.createDirectories()
url.openStream().use { it.copyTo(cordaJar) } cordappDirectory.createDirectories()
Authenticator.setDefault(object : Authenticator() {
override fun getPasswordAuthentication(): PasswordAuthentication {
return PasswordAuthentication(System.getenv("CORDA_ARTIFACTORY_USERNAME"), System.getenv("CORDA_ARTIFACTORY_PASSWORD").toCharArray())
}
})
artifactUrlMap!!.forEach { artifactName, artifactUrl ->
artifactUrl.openStream().use {
if (artifactName.startsWith("corda-finance")) {
it.copyTo(cordappDirectory / "$artifactName.jar", StandardCopyOption.REPLACE_EXISTING)
}
else it.copyTo(path / "$artifactName.jar", StandardCopyOption.REPLACE_EXISTING)
}
}
} catch (e: Exception) { } catch (e: Exception) {
if ("HTTP response code: 401" in e.message!!) { if ("HTTP response code: 401" in e.message!!) {
log.warn("CORDA_ARTIFACTORY_USERNAME ${System.getenv("CORDA_ARTIFACTORY_USERNAME")}") log.warn("CORDA_ARTIFACTORY_USERNAME ${System.getenv("CORDA_ARTIFACTORY_USERNAME")}")
@ -105,9 +122,11 @@ class Distribution private constructor(
*/ */
override fun toString() = "Corda(version = $version, path = $cordaJar)" override fun toString() = "Corda(version = $version, path = $cordaJar)"
enum class Type { enum class Type(val artifacts: Set<String>) {
CORDA_OS, CORDA_OS(setOf("corda", "corda-webserver", "corda-finance")),
CORDA_ENTERPRISE // bridge-server not available in Enterprise Dev Previews
// migration-tool not published in Enterprise Dev Previews
CORDA_ENTERPRISE(setOf("corda", "corda-webserver", "corda-finance", "corda-bridgeserver", "migration-tool"))
} }
companion object { companion object {
@ -122,22 +141,43 @@ class Distribution private constructor(
val R3_MASTER = fromJarFile(Type.CORDA_ENTERPRISE, "r3corda-master") val R3_MASTER = fromJarFile(Type.CORDA_ENTERPRISE, "r3corda-master")
/** /**
* Get representation of a Corda distribution from Artifactory based on its version string. * Get all jar artifacts from Artifactory for a given distribution and version of Corda.
* @param type The Corda distribution type. * @param type The Corda distribution type.
* @param version The version of the Corda distribution. * @param version The version of the Corda distribution.
*/ */
fun fromArtifactory(type: Type, version: String): Distribution { fun fromArtifactory(type: Type, version: String): Distribution {
val url = val artifactUrlMap = when (type) {
when (type) { Type.CORDA_OS -> resolveArtifacts(Type.CORDA_OS.artifacts, version, "https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda")
Type.CORDA_OS -> URL("https://ci-artifactory.corda.r3cev.com/artifactory/corda-releases/net/corda/corda/$version/corda-$version.jar") Type.CORDA_ENTERPRISE -> {
Type.CORDA_ENTERPRISE -> URL("https://ci-artifactory.corda.r3cev.com/artifactory/r3-corda-releases/com/r3/corda/corda/$version/corda-$version.jar") Authenticator.setDefault(object : Authenticator() {
override fun getPasswordAuthentication(): PasswordAuthentication {
return PasswordAuthentication(System.getenv("CORDA_ARTIFACTORY_USERNAME"), System.getenv("CORDA_ARTIFACTORY_PASSWORD").toCharArray())
} }
log.info("Artifactory URL: $url\n") })
val distribution = Distribution(type, version, url = url) resolveArtifacts(Type.CORDA_ENTERPRISE.artifacts, version, "https://ci-artifactory.corda.r3cev.com/artifactory/r3-corda-releases/com/r3/corda")
}
}
val distribution = Distribution(type, version, artifactUrlMap = artifactUrlMap)
distributions.add(distribution) distributions.add(distribution)
return distribution return distribution
} }
private fun resolveArtifacts(artifacts: Set<String>, version: String, artifactoryBaseUrl: String) : Map<String,URL> {
val urlMap = mutableMapOf<String,URL>()
artifacts.forEach { artifact ->
val url = URL("$artifactoryBaseUrl/$artifact/$version/$artifact-$version.jar")
log.info("Artifactory resource URL: $url")
try {
url.openStream()
urlMap[artifact] = url
}
catch (ex: IOException) {
log.warn("Unable to open artifactory resource URL: ${ex.message}")
}
}
return urlMap
}
/** /**
* Get representation of a Corda distribution based on its version string and fat JAR path. * Get representation of a Corda distribution based on its version string and fat JAR path.
* @param type The Corda distribution type. * @param type The Corda distribution type.
@ -166,13 +206,24 @@ class Distribution private constructor(
* Get registered representation of a Corda distribution based on its version string. * Get registered representation of a Corda distribution based on its version string.
* @param version The version of the Corda distribution * @param version The version of the Corda distribution
*/ */
private val entVersionScheme = "^\\d\\.\\d\\.\\d.*".toRegex() // ENTERPRISE version scheme x.y.z,
private val osVersionScheme = "^\\d\\.\\d.*".toRegex() // OS version scheme x.y
fun fromVersionString(version: String): Distribution = when (version) { fun fromVersionString(version: String): Distribution = when (version) {
"master" -> MASTER "master" -> MASTER
"r3-master" -> R3_MASTER "r3-master" -> R3_MASTER
"corda-3.0" -> fromArtifactory(Type.CORDA_OS, version) "corda-3.0" -> fromArtifactory(Type.CORDA_OS, version)
"corda-3.1" -> fromArtifactory(Type.CORDA_OS, version) "3.1-corda" -> fromArtifactory(Type.CORDA_OS, version)
"R3.CORDA-3.0.0-DEV-PREVIEW-3" -> fromArtifactory(Type.CORDA_ENTERPRISE, version) "R3.CORDA-3.0.0-DEV-PREVIEW-3" -> fromArtifactory(Type.CORDA_ENTERPRISE, version)
else -> distributions.firstOrNull { it.version == version } ?: throw CordaRuntimeException("Unable to locate Corda distribution for version: $version") else -> {
if (version.matches(entVersionScheme))
fromArtifactory(Type.CORDA_ENTERPRISE, version)
else if (version.matches(osVersionScheme))
fromArtifactory(Type.CORDA_OS, version)
else
distributions.firstOrNull { it.version == version }
?: throw CordaRuntimeException("Invalid Corda distribution for specified version: $version")
}
} }
} }
} }

View File

@ -45,7 +45,7 @@ import java.util.concurrent.CountDownLatch
*/ */
class Node( class Node(
val config: Configuration, val config: Configuration,
private val rootDirectory: Path = currentDirectory, val rootDirectory: Path = currentDirectory,
private val settings: ServiceSettings = ServiceSettings(), private val settings: ServiceSettings = ServiceSettings(),
val rpcProxy: Boolean = false, val rpcProxy: Boolean = false,
val networkType: Distribution.Type val networkType: Distribution.Type
@ -95,13 +95,13 @@ class Node(
log.info("Configuring {} ...", this) log.info("Configuring {} ...", this)
serviceDependencies.addAll(config.database.type.dependencies(config)) serviceDependencies.addAll(config.database.type.dependencies(config))
config.distribution.ensureAvailable() config.distribution.ensureAvailable()
if (networkType == Distribution.Type.CORDA_OS) { if (networkType == Distribution.Type.CORDA_ENTERPRISE && System.getProperty("USE_NETWORK_SERVICES") != null) {
config.writeToFile(rootDirectory / "${config.name}_node.conf")
}
else {
val nodeDirectory = (rootDirectory / config.name).createDirectories() val nodeDirectory = (rootDirectory / config.name).createDirectories()
config.writeToFile(nodeDirectory / "node.conf") config.writeToFile(nodeDirectory / "node.conf")
} }
else {
config.writeToFile(rootDirectory / "${config.name}_node.conf")
}
installApps() installApps()
} }
@ -273,8 +273,7 @@ class Node(
} }
private fun installApps() { private fun installApps() {
val version = config.distribution.version val appDirectory = config.distribution.cordappDirectory
val appDirectory = stagingRoot / "corda" / version / "apps"
if (appDirectory.exists()) { if (appDirectory.exists()) {
val targetAppDirectory = runtimeDirectory / "cordapps" val targetAppDirectory = runtimeDirectory / "cordapps"
FileUtils.copyDirectory(appDirectory.toFile(), targetAppDirectory.toFile()) FileUtils.copyDirectory(appDirectory.toFile(), targetAppDirectory.toFile())
@ -387,9 +386,10 @@ class Node(
fun build(): Node { fun build(): Node {
val name = name ?: error("Node name not set") val name = name ?: error("Node name not set")
val directory = directory ?: error("Runtime directory not set") val directory = directory ?: error("Runtime directory not set")
val compatibilityZoneURL = // TODO: rework how we use the Doorman/NMS (now these are a separate product / distribution)
if (networkType == Distribution.Type.CORDA_ENTERPRISE) val compatibilityZoneURL = null
compatibilityZoneURL ?: "http://localhost:1300" if (networkType == Distribution.Type.CORDA_ENTERPRISE && System.getProperty("USE_NETWORK_SERVICES") != null)
"http://localhost:1300" // TODO: add additional USE_NETWORK_SERVICES_URL to specify location of existing operational environment to use.
else null else null
return Node( return Node(
Configuration( Configuration(

View File

@ -36,7 +36,7 @@ class Configuration(
vararg configElements: ConfigurationTemplate vararg configElements: ConfigurationTemplate
) { ) {
private val developerMode = (distribution.type == Distribution.Type.CORDA_OS) private val developerMode = System.getProperty("USE_NETWORK_SERVICES") == null
val cordaX500Name: CordaX500Name by lazy({ val cordaX500Name: CordaX500Name by lazy({
CordaX500Name(name, location, country) CordaX500Name(name, location, country)

View File

@ -60,7 +60,7 @@ open class Command(
private val thread = Thread(Runnable { private val thread = Thread(Runnable {
try { try {
log.info("Command: $command") log.info("Executing command: $command from directory: $directory")
val processBuilder = ProcessBuilder(command) val processBuilder = ProcessBuilder(command)
.directory(directory.toFile()) .directory(directory.toFile())
.redirectErrorStream(true) .redirectErrorStream(true)

View File

@ -1,3 +1,4 @@
minimumPlatformVersion = 1 minimumPlatformVersion = 1
maxMessageSize = 10485760 maxMessageSize = 10485760
maxTransactionSize = 10485760 maxTransactionSize = 10485760
eventHorizonDays = 30

View File

@ -5,3 +5,4 @@ notaries : [{
minimumPlatformVersion = 1 minimumPlatformVersion = 1
maxMessageSize = 10485760 maxMessageSize = 10485760
maxTransactionSize = 10485760 maxTransactionSize = 10485760
eventHorizonDays = 30

View File

@ -10,13 +10,13 @@
package net.corda.behave.scenarios.helpers package net.corda.behave.scenarios.helpers
import net.corda.behave.logging.getLogger
import net.corda.behave.scenarios.ScenarioState import net.corda.behave.scenarios.ScenarioState
import net.corda.core.messaging.CordaRPCOps import net.corda.core.messaging.CordaRPCOps
import net.corda.core.utilities.contextLogger
abstract class Substeps(protected val state: ScenarioState) { abstract class Substeps(protected val state: ScenarioState) {
protected val log = contextLogger() protected val log = getLogger<Substeps>()
protected fun withNetwork(action: ScenarioState.() -> Unit) = protected fun withNetwork(action: ScenarioState.() -> Unit) =
state.withNetwork(action) state.withNetwork(action)

View File

@ -14,7 +14,7 @@ cd ${BUILD_DIR}
../../gradlew behaveJar ../../gradlew behaveJar
BEHAVE_JAR=$(ls build/libs/corda-behave-*.jar | tail -n1) BEHAVE_JAR=$(ls build/libs/corda-behave-*.jar | tail -n1)
STAGING_ROOT=~/staging STAGING_ROOT="${STAGING_ROOT:-TMPDIR/staging}"
# startup # startup
java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} --glue net.corda.behave.scenarios -path ./src/scenario/resources/features/startup/logging.feature java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} --glue net.corda.behave.scenarios -path ./src/scenario/resources/features/startup/logging.feature

View File

@ -0,0 +1,34 @@
package net.corda.behave.node
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import kotlin.test.assertNotNull
class DistributionTest {
/**
* NOTE: ensure you have correctly setup the system environment variables:
* CORDA_ARTIFACTORY_USERNAME
* CORDA_ARTIFACTORY_PASSWORD
*/
@Test
fun `resolve OS distribution from Artifactory`() {
val distribution = Distribution.fromArtifactory(Distribution.Type.CORDA_OS, "3.1-corda")
assertNotNull(distribution.artifactUrlMap)
assertThat(distribution.artifactUrlMap!!.size).isGreaterThanOrEqualTo(3)
// -DSTAGING_ROOT=${STAGING_ROOT}
distribution.ensureAvailable()
println("Check contents of ${distribution.path}")
}
@Test
fun `resolve Enterprise distribution from Artifactory`() {
val distribution = Distribution.fromArtifactory(Distribution.Type.CORDA_ENTERPRISE, "3.0.0-RC01")
assertNotNull(distribution.artifactUrlMap)
assertThat(distribution.artifactUrlMap!!.size).isGreaterThanOrEqualTo(5)
// -DSTAGING_ROOT=${STAGING_ROOT}
distribution.ensureAvailable()
println("Check contents of ${distribution.path}")
}
}

View File

@ -1,3 +1,6 @@
Overview
========
The Behave Cucumber Scenarios defined under these sub-directories exercise Corda Enterprise and OS Corda distributions to include: The Behave Cucumber Scenarios defined under these sub-directories exercise Corda Enterprise and OS Corda distributions to include:
- Open Source master - Open Source master
- Corda Enterprise master - Corda Enterprise master
@ -31,3 +34,196 @@ Further goals of this project are to:
- effect execution of scenarios in specified Target Environments - effect execution of scenarios in specified Target Environments
- use Before and After hooks to pre-configure Target Environments to allow batch running of multiple scenarios (eg. a given - use Before and After hooks to pre-configure Target Environments to allow batch running of multiple scenarios (eg. a given
feature will specify the environment once for one or many scenarios) feature will specify the environment once for one or many scenarios)
QA setup and usage instructions
===============================
Setup
-----
Set up the staging area which will hold all the distributions of Corda referenced in the test scenarios:
- specify the staging root directory:
```bash
$export STAGING_ROOT=$HOME/staging
```
- if any of your tests are referencing a master version of [Corda OS](https://github.com/corda/corda), checkout the latest version of the Open Source repo and run:
```bash
$ cd experimental/behave
$ ./prepare.sh
```
You should now see the following artifacts in your staging area:
```bash
$ ls -lR /Users/home/staging/corda/corda-master
total 285440
drwxr-xr-x 3 user staff 96 4 Jun 16:11 apps
-rw-r--r-- 1 user staff 55439705 4 Jun 16:11 corda.jar
-rw-r--r-- 1 user staff 90699598 4 Jun 16:11 network-bootstrapper.jar
/Users/home/staging/corda/corda-master/apps:
total 4688
-rw------- 1 user staff 2396954 4 Jun 16:11 corda-finance-4.0-SNAPSHOT.jar
```
- similarly, if any of your tests are referencing a master version of [Corda Enterprise](https://github.com/corda/enterprise), checkout the latest version of the Enterprise repo and run:
```bash
$ cd experimental/behave
$ ./prepare.sh
```
You should now see the following artifacts in your staging area:
```bash
$ ls -lR /Users/home/staging/corda/r3corda-master/
total 503264
drwxr-xr-x 6 user staff 192 4 Jun 16:39 apps
-rw-r--r-- 1 user staff 6239281 4 Jun 16:39 corda-rpcProxy.jar
-rw-r--r-- 1 user staff 66441262 4 Jun 16:38 corda.jar
-rw-r--r-- 1 user staff 71987460 4 Jun 16:39 dbmigration.jar
-rw-r--r-- 1 user staff 112985324 4 Jun 16:39 network-bootstrapper.jar
-rwxr-xr-x 1 user staff 1224 4 Jun 16:39 startRPCproxy.sh
/Users/home/staging/corda/r3corda-master/apps:
total 82248
-rw------- 1 user staff 2418144 4 Jun 16:38 corda-finance-R3.CORDA-3.0-SNAPSHOT.jar
```
Notes:
- all versions of Corda published to the Artifactory release repositories are automatically downloaded and configured
on the fly as part of a test scenario.
- Docker must be running for SQL Server, Oracle and PostrgeSQL tests.
Usage
-----
Checkout the latest version of the Enterprise repo and run:
```bash
$cd experimental/behave
../../gradlew behaveJar
````
Change to the QA testing directory containing the scenario scripts and run:
```bash
$cd ../../test/qa
$export BEHAVE_JAR=$(ls ../../../experimental/behave/build/libs/corda-behave-*.jar | tail -n1)
```
To verify the tool is ready to be used run:
```bash
$ java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR}
```
You should now see the following output providing details on how to run the tool:
```bash
Missing required option(s) [path]
Usage: ScenarioRunner [options] --path <location of feature scenario definitions>
Examples:
ScenarioRunner -path <features-dir>
ScenarioRunner -path <features-dir>/<name>.feature
ScenarioRunner -path <features-dir>/<name>.feature:3:9
ScenarioRunner -path <features-dir> --plugin html --tags @qa
ScenarioRunner -path <features-dir> --plugin html --tags @compatibility
Please refer to the Cucumber documentation https://cucumber.io/docs/reference/jvm for more info.
Option (* = required) Description
--------------------- -----------
-d
--glue [location of additional step (default: net.corda.behave.scenarios)
definitions, hooks and plugins]
* --path <Path location of .feature
specifications>
--plugin [register additional plugins (default: pretty)
(see https://cucumber.
io/docs/reference/jvm)]
--tags [only run scenarios marked as
@<tag-name>]
```
Note: passing in a *-d* option will perform a dry run only (validates the syntax of the scenario but does not execute the code)
Scenario scripts
----------------
There are currently two sets of scripts:
1. Functional
```bash
# the Cucumber behave test scenario definitions themselves
$ls -l functional/resources/features/functional.feature
# Unix script to easily run these
$ls -l functional/resources/scripts/run-functional.sh
```
2. Interoperability & Compatibility
```bash
# the Cucumber behave test scenario definitions themselves
$ls -l compatibility/resources/features/interoperability.feature
# Unix script to easily run these
$ls -l compatibility/resources/scripts/run-interoperability.sh
```
Note: the complete suite of Compatibility test scenarios have note yet been fully implemented.
You can now run the above test scripts in a number of different ways:
* as a complete suite called a *feature set*:
```bash
# run all scenarios in the functional.feature file
$java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} -path functional/resources/features/functional.feature
```
```bash
# run all scenarios in the interoperability.feature file
$java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} -path compatibility/resources/features/interoperability.feature
```
* as a set of tagged scenarios within a feature file:
```bash
# run all scenarios tagged with qa in the interoperability.feature file
$java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} -path compatibility/resources/features/interoperability.feature:@qa
```
* as an individual scenario for all example configurations:
```bash
# run the scenario defined on line 5 of the interoperability.feature file
$java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} -path compatibility/resources/features/interoperability.feature:5
# by default the above will run as many times as there are parameterised variable definitions
Examples:
| R3-Corda-Node-Version | Corda-Node-Version | Currency |
| r3-master | corda-master | GBP |
| corda-3.0 | corda-3.1 | GBP |
| R3.CORDA-3.0.0-DEV-PREVIEW-3 | corda-3.0 | GBP |
| R3.CORDA-3.0.0-DEV-PREVIEW-3 | corda-3.1 | GBP |
```
* as an individual scenario for a single example configuration you will need to comment out the other example line items:
```bash
# run the scenario defined on line 5 of the interoperability.feature file
$java -DSTAGING_ROOT=${STAGING_ROOT} -jar ${BEHAVE_JAR} -path compatibility/resources/features/interoperability.feature:5
# by default the above will the scenario using *corda-3.0* and *corda-3.1* node versions only:
Examples:
| R3-Corda-Node-Version | Corda-Node-Version | Currency |
# | r3-master | corda-master | GBP |
| corda-3.0 | corda-3.1 | GBP |
# | R3.CORDA-3.0.0-DEV-PREVIEW-3 | corda-3.0 | GBP |
# | R3.CORDA-3.0.0-DEV-PREVIEW-3 | corda-3.1 | GBP |
```

View File

@ -12,5 +12,8 @@ BEHAVE_DIR=${R3CORDA_HOME}/experimental/behave
cd ${BEHAVE_DIR} cd ${BEHAVE_DIR}
../../gradlew behaveJar ../../gradlew behaveJar
# QA interoperability BEHAVE_JAR=$(ls build/libs/corda-behave-*.jar | tail -n1)
java -jar ${BEHAVE_DIR}/build/libs/corda-behave.jar -d --glue net.corda.behave.scenarios -path ${R3CORDA_HOME}/testing/qa/behave/compatibility/resources/features/interoperability.feature STAGING_ROOT="${STAGING_ROOT:-TMPDIR/staging}"
# QA interoperability (specify -d for dry-run)
java -DSTAGING_ROOT=${STAGING_ROOT} -DDISABLE_CLEANUP=true -jar ${BEHAVE_JAR} --glue net.corda.behave.scenarios -path ${R3CORDA_HOME}/testing/qa/behave/compatibility/resources/features/interoperability.feature

View File

@ -7,12 +7,12 @@ Feature: QA Operational
And node PartyA has the finance app installed And node PartyA has the finance app installed
When the network is ready When the network is ready
Then node PartyA is on platform version 4 Then node PartyA is on platform version 4
And node PartyA is on release version R3.CORDA-3.0.0-DEV-PREVIEW-3 And node PartyA is on release version <Version-label>
And user can retrieve node identity information for node PartyA And user can retrieve node identity information for node PartyA
Examples: Examples:
| R3-Corda-Node-Version | | R3-Corda-Node-Version | Version-label |
| R3.CORDA-3.0.0-DEV-PREVIEW-3 | | r3-master | 3.0.0-SNAPSHOT |
Scenario Outline: QA: Stand up a basic Corda Enterprise Network with one node and a notary; node can issue cash to itself Scenario Outline: QA: Stand up a basic Corda Enterprise Network with one node and a notary; node can issue cash to itself
Given a node PartyA of version <R3-Corda-Node-Version> with proxy Given a node PartyA of version <R3-Corda-Node-Version> with proxy
@ -23,7 +23,7 @@ Feature: QA Operational
Examples: Examples:
| R3-Corda-Node-Version | Currency | | R3-Corda-Node-Version | Currency |
| R3.CORDA-3.0.0-DEV-PREVIEW-3 | GBP | | r3-master | GBP |
Scenario Outline: User can connect to a Corda Enterprise node using a SQL Server database Scenario Outline: User can connect to a Corda Enterprise node using a SQL Server database
Given a node PartyA of version <Node-Version> Given a node PartyA of version <Node-Version>
@ -33,7 +33,7 @@ Feature: QA Operational
Examples: Examples:
| Node-Version | Database-Type | | Node-Version | Database-Type |
| R3.CORDA-3.0.0-DEV-PREVIEW-3 | SQL Server | | r3-master | SQL Server |
Scenario Outline: QA: Node using H2 can transact with node using SQL Server, in a Corda Enterprise configured network. Scenario Outline: QA: Node using H2 can transact with node using SQL Server, in a Corda Enterprise configured network.
Given a node PartyA of version <R3-Corda-Node-Version> with proxy Given a node PartyA of version <R3-Corda-Node-Version> with proxy
@ -48,7 +48,7 @@ Feature: QA Operational
Examples: Examples:
| R3-Corda-Node-Version | Currency | Database-Type | | R3-Corda-Node-Version | Currency | Database-Type |
| R3.CORDA-3.0.0-DEV-PREVIEW-3 | GBP | SQL Server | | r3-master | GBP | SQL Server |
Scenario Outline: User can connect to a Corda Enterprise node using a PostgreSQL database Scenario Outline: User can connect to a Corda Enterprise node using a PostgreSQL database
Given a node PartyA of version <Node-Version> Given a node PartyA of version <Node-Version>
@ -58,7 +58,7 @@ Feature: QA Operational
Examples: Examples:
| Node-Version | Database-Type | | Node-Version | Database-Type |
| R3.CORDA-3.0.0-DEV-PREVIEW-3 | postgres | | r3-master | postgres |
Scenario Outline: QA: Node using H2 can transact with node using Postgres, in a Corda Enterprise configured network. Scenario Outline: QA: Node using H2 can transact with node using Postgres, in a Corda Enterprise configured network.
Given a node PartyA of version <R3-Corda-Node-Version> with proxy Given a node PartyA of version <R3-Corda-Node-Version> with proxy
@ -73,4 +73,4 @@ Feature: QA Operational
Examples: Examples:
| R3-Corda-Node-Version | Currency | Database-Type | | R3-Corda-Node-Version | Currency | Database-Type |
| R3.CORDA-3.0.0-DEV-PREVIEW-3 | GBP | postgres | | r3-master | GBP | postgres |

View File

@ -1,11 +1,11 @@
#!/bin/bash #!/bin/bash
# #
# Run this script from your ${R3CORDA_HOME}/experimental/behave directory, where R3CORDA_HOME refers to Corda Enterprise source code (eg. GitHub master, branch or TAG) # Run this script from your $R3CORDA_HOME directory, where R3CORDA_HOME refers to R3 Corda source code (eg. GitHub master, branch or TAG)
# For example: # For example:
# R3CORDA_HOME => git clone https://github.com/corda/enterprise # R3CORDA_HOME => git clone https://github.com/corda/enterprise
# #
# $ testing/qa/behave/compatibility/resources/scripts/run-functional.sh # $ testing/qa/behave/functional/resources/scripts/run-functional.sh
R3CORDA_HOME=$PWD R3CORDA_HOME=$PWD
@ -13,5 +13,8 @@ BEHAVE_DIR=${R3CORDA_HOME}/experimental/behave
cd ${BEHAVE_DIR} cd ${BEHAVE_DIR}
../../gradlew behaveJar ../../gradlew behaveJar
# QA functional BEHAVE_JAR=$(ls build/libs/corda-behave-*.jar | tail -n1)
java -jar ${BEHAVE_DIR}/build/libs/corda-behave.jar -d --glue net.corda.behave.scenarios -path ${R3CORDA_HOME}/testing/qa/behave/functional/resources/features/functional.feature STAGING_ROOT="${STAGING_ROOT:-TMPDIR/staging}"
# QA functional (specify -d for dry-run)
java -DSTAGING_ROOT=${STAGING_ROOT} -DDISABLE_CLEANUP=true -jar ${BEHAVE_JAR} --glue net.corda.behave.scenarios -path ${R3CORDA_HOME}/testing/qa/behave/functional/resources/features/functional.feature

View File

@ -12,7 +12,11 @@
VERSION=master VERSION=master
BUILD_DIR=`pwd` BUILD_DIR=`pwd`
STAGING_DIR=~/staging/corda/corda-${VERSION} STAGING_DIR="${STAGING_ROOT:-$TMPDIR/staging}"
echo "Staging directory: $STAGING_DIR"
CORDA_DIR=${STAGING_DIR}/corda/corda-${VERSION}
echo "Corda staging directory: $CORDA_DIR"
# Set up directories # Set up directories
mkdir -p ${STAGING_DIR}/apps mkdir -p ${STAGING_DIR}/apps
@ -21,19 +25,19 @@ cd ${BUILD_DIR}
echo "*************************************************************" echo "*************************************************************"
echo "Building and installing $VERSION from $BUILD_DIR" echo "Building and installing $VERSION from $BUILD_DIR"
echo " to $STAGING_DIR" echo " to $CORDA_DIR"
echo "*************************************************************" echo "*************************************************************"
# Copy Corda capsule into deps # Copy Corda capsule into deps
./gradlew clean install ./gradlew clean install
cp -v $(ls node/capsule/build/libs/corda-*.jar | tail -n1) ${STAGING_DIR}/corda.jar cp -v $(ls node/capsule/build/libs/corda-*.jar | tail -n1) ${CORDA_DIR}/corda.jar
cp -v $(ls finance/build/libs/corda-finance-*.jar | tail -n1) ${STAGING_DIR}/apps cp -v $(ls finance/build/libs/corda-finance-*.jar | tail -n1) ${CORDA_DIR}/apps
# Build Network Bootstrapper # Build Network Bootstrapper
./gradlew buildBootstrapperJar ./gradlew buildBootstrapperJar
cp -v $(ls tools/bootstrapper/build/libs/*.jar | tail -n1) ${STAGING_DIR}/network-bootstrapper.jar cp -v $(ls tools/bootstrapper/build/libs/*.jar | tail -n1) ${CORDA_DIR}/network-bootstrapper.jar
# Build rpcProxy (required by CTS Scenario Driver to call Corda 3.0 which continues to use Kryo for RPC) # Build rpcProxy (required by CTS Scenario Driver to call Corda 3.0 which continues to use Kryo for RPC)
./gradlew testing:qa:behave:tools:rpc-proxy:rpcProxyJar ./gradlew testing:qa:behave:tools:rpc-proxy:rpcProxyJar
cp -v $(ls testing/qa/behave/tools/rpc-proxy/build/libs/corda-rpcProxy*.jar | tail -n1) ${STAGING_DIR}/corda-rpcProxy.jar cp -v $(ls testing/qa/behave/tools/rpc-proxy/build/libs/corda-rpcProxy*.jar | tail -n1) ${CORDA_DIR}/corda-rpcProxy.jar
cp -v testing/qa/behave/tools/rpc-proxy/startRPCproxy.sh ${STAGING_DIR} cp -v testing/qa/behave/tools/rpc-proxy/startRPCproxy.sh ${CORDA_DIR}