Slim down bridge capsule jar

Add bridge smoke-test to :bridge:bridgecapsule to verify that all the dependencies are correctly packaged and present.

Correct proton-j version

Correct proton-j version
This commit is contained in:
Matthew Nesbit 2018-05-03 14:24:27 +01:00
parent 081b7251a7
commit 1ae4c20b10
6 changed files with 356 additions and 2 deletions

View File

@ -10,6 +10,7 @@
* This build.gradle exists to publish our capsule (executable fat jar) to maven. It cannot be placed in the
* bridges project because the bintray plugin cannot publish two modules from one project.
*/
apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'us.kirchmeier.capsule'
apply plugin: 'com.jfrog.artifactory'
@ -19,11 +20,48 @@ description 'Corda bridge server capsule'
configurations {
runtimeArtifacts
capsuleRuntime
smokeTestCompile.extendsFrom testCompile
smokeTestRuntime.extendsFrom testRuntime
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
sourceSets {
smokeTest {
kotlin {
// We must NOT have any Bridge code on the classpath, so do NOT
// include the test or integrationTest dependencies here.
compileClasspath += main.output
runtimeClasspath += main.output
srcDir file('src/smoke-test/kotlin')
}
resources {
srcDir file('src/smoke-test/resources')
}
}
}
dependencies {
// TypeSafe Config: for simple and human friendly config files.
capsuleRuntime "com.typesafe:config:$typesafe_config_version"
// Smoke tests do NOT have any Node code on the classpath!
smokeTestCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
smokeTestCompile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
smokeTestCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
smokeTestCompile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
smokeTestCompile "org.apache.logging.log4j:log4j-core:$log4j_version"
smokeTestCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
smokeTestCompile project(':node-driver')
smokeTestCompile project(':test-utils')
smokeTestCompile "org.apache.curator:curator-test:${curator_version}"
smokeTestCompile "junit:junit:$junit_version"
}
// Force the Caplet to target Java 6. This ensures that running 'java -jar corda.jar' on any Java 6 VM upwards
@ -65,6 +103,19 @@ task buildBridgeServerJar(type: FatCapsule, dependsOn: project(':bridge').jar) {
}
}
processSmokeTestResources {
from(project.tasks['buildBridgeServerJar']) {
rename 'corda-bridgeserver-(.*)', 'corda-bridgeserver.jar'
into "net/corda/bridge/smoketest"
}
}
task smokeTest(type: Test) {
testClassesDirs = sourceSets.smokeTest.output.classesDirs
classpath = sourceSets.smokeTest.runtimeClasspath
}
artifacts {
runtimeArtifacts buildBridgeServerJar
publish buildBridgeServerJar {

View File

@ -0,0 +1,241 @@
package net.corda.bridge.smoketest
import com.nhaarman.mockito_kotlin.doReturn
import com.nhaarman.mockito_kotlin.whenever
import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.*
import net.corda.core.node.NetworkParameters
import net.corda.core.node.NotaryInfo
import net.corda.core.node.services.AttachmentId
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.serialize
import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger
import net.corda.node.services.config.CertChainPolicyConfig
import net.corda.node.services.config.EnterpriseConfiguration
import net.corda.node.services.config.MutualExclusionConfiguration
import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.messaging.ArtemisMessagingServer
import net.corda.nodeapi.internal.*
import net.corda.nodeapi.internal.bridging.BridgeControl
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.crypto.*
import net.corda.nodeapi.internal.network.NetworkParametersCopier
import net.corda.testing.core.*
import net.corda.testing.internal.rigorousMock
import org.apache.activemq.artemis.api.core.RoutingType
import org.apache.activemq.artemis.api.core.SimpleString
import org.apache.curator.test.TestingServer
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.net.Socket
import java.nio.file.Path
import java.nio.file.Paths
import java.security.cert.X509Certificate
import java.time.Instant
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import kotlin.streams.toList
class BridgeSmokeTest {
companion object {
val log = contextLogger()
}
@Rule
@JvmField
val tempFolder = TemporaryFolder()
@Rule
@JvmField
val serializationEnvironment = SerializationEnvironmentRule(true)
private abstract class AbstractNodeConfiguration : NodeConfiguration
@Before
fun setup() {
}
@After
fun cleanup() {
}
@Test
fun `Run full features bridge from jar to ensure everything works`() {
val artemisConfig = object : NodeSSLConfiguration {
override val baseDirectory: Path = tempFolder.root.toPath()
override val keyStorePassword: String = "cordacadevpass"
override val trustStorePassword: String = "trustpass"
override val crlCheckSoftFail: Boolean = true
}
artemisConfig.createBridgeKeyStores(DUMMY_BANK_A_NAME)
copyBridgeResource("corda-bridgeserver.jar")
copyBridgeResource("bridge.conf")
createNetworkParams(tempFolder.root.toPath())
val (artemisServer, artemisClient) = createArtemis()
val zkServer = TestingServer(11105, false)
try {
installBridgeControlResponder(artemisClient)
zkServer.start()
val bridge = startBridge(tempFolder.root.toPath())
waitForBridge(bridge)
} finally {
zkServer.close()
artemisClient.stop()
artemisServer.stop()
}
}
private fun copyBridgeResource(resourceName: String) {
val testDir = tempFolder.root.toPath()
// Find the finance jar file for the smoke tests of this module
val bridgeJar = Paths.get("build", "resources/smokeTest/net/corda/bridge/smoketest").list {
it.filter { resourceName in it.toString() }.toList().single()
}
bridgeJar.copyToDirectory(testDir)
}
fun createNetworkParams(baseDirectory: Path) {
val dummyNotaryParty = TestIdentity(DUMMY_NOTARY_NAME)
val notaryInfo = NotaryInfo(dummyNotaryParty.party, false)
val copier = NetworkParametersCopier(NetworkParameters(
minimumPlatformVersion = 1,
notaries = listOf(notaryInfo),
modifiedTime = Instant.now(),
maxMessageSize = 10485760,
maxTransactionSize = 40000,
epoch = 1,
whitelistedContractImplementations = emptyMap<String, List<AttachmentId>>()
), overwriteFile = true)
copier.install(baseDirectory)
}
fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
certificatesDirectory.createDirectories()
if (!trustStoreFile.exists()) {
loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/${DEV_CA_TRUST_STORE_FILE}"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword)
}
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
val sslKeyStore = loadSslKeyStore(createNew = true)
sslKeyStore.update {
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
setPrivateKey(
X509Utilities.CORDA_CLIENT_TLS,
tlsKeyPair.private,
listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert))
}
}
private fun startBridge(baseDirectory: Path): Process {
val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java")
val builder = ProcessBuilder()
.command(javaPath.toString(), "-Dcapsule.log=verbose",
"-jar", "corda-bridgeserver.jar")
.directory(baseDirectory.toFile())
.inheritIO()
builder.environment().putAll(mapOf(
"CAPSULE_CACHE_DIR" to (baseDirectory / "capsule").toString()
))
log.info("Start bridge process in $baseDirectory")
return builder.start()
}
private fun waitForBridge(process: Process) {
var ok = false
val executor = Executors.newSingleThreadScheduledExecutor()
try {
executor.scheduleWithFixedDelay({
try {
if (!process.isAlive) {
log.error("Bridge has died.")
return@scheduleWithFixedDelay
}
if (!serverListening("localhost", 10005)) {
log.warn("Bridge not listening yet")
return@scheduleWithFixedDelay
}
ok = true
// Cancel the polling
executor.shutdown()
} catch (e: Exception) {
log.warn("Bridge not ready yet (Error: {})", e.message)
}
}, 5, 1, TimeUnit.SECONDS)
val setupOK = executor.awaitTermination(60, TimeUnit.SECONDS)
check(setupOK && ok && process.isAlive) { "Bridge Failed to open listening port" }
} catch (e: Exception) {
throw e
} finally {
executor.shutdownNow()
process.destroyForcibly()
}
}
fun serverListening(host: String, port: Int): Boolean {
var s: Socket? = null
try {
s = Socket(host, port)
return true
} catch (e: Exception) {
return false
} finally {
try {
s?.close()
} catch (e: Exception) {
}
}
}
private fun createArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
doReturn(ALICE_NAME).whenever(it).myLegalName
doReturn("trustpass").whenever(it).trustStorePassword
doReturn("cordacadevpass").whenever(it).keyStorePassword
doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
doReturn(null).whenever(it).jmxMonitoringHttpPort
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
}
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
artemisServer.start()
artemisClient.start()
return Pair(artemisServer, artemisClient)
}
private fun installBridgeControlResponder(artemisClient: ArtemisMessagingClient) {
val artemis = artemisClient.started!!
val inboxAddress = SimpleString("${ArtemisMessagingComponent.P2P_PREFIX}Test")
artemis.session.createQueue(inboxAddress, RoutingType.ANYCAST, inboxAddress, true)
artemis.session.createQueue(ArtemisMessagingComponent.BRIDGE_NOTIFY, RoutingType.ANYCAST, ArtemisMessagingComponent.BRIDGE_NOTIFY, false)
val controlConsumer = artemis.session.createConsumer(ArtemisMessagingComponent.BRIDGE_NOTIFY)
controlConsumer.setMessageHandler { msg ->
val bridgeControl = BridgeControl.NodeToBridgeSnapshot("Test", listOf(inboxAddress.toString()), emptyList())
val controlPacket = bridgeControl.serialize(context = SerializationDefaults.P2P_CONTEXT).bytes
val artemisMessage = artemis.session.createMessage(false)
artemisMessage.writeBodyBufferBytes(controlPacket)
artemis.producer.send(ArtemisMessagingComponent.BRIDGE_CONTROL, artemisMessage)
msg.acknowledge()
}
}
}

View File

@ -0,0 +1,26 @@
//
// R3 Proprietary and Confidential
//
// Copyright (c) 2018 R3 Limited. All rights reserved.
//
// The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
//
// Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
bridgeMode = SenderReceiver
outboundConfig : {
artemisBrokerAddress = "localhost:11005"
socksProxyConfig : {
version = SOCKS5
proxyAddress = "localhost:12345"
userName = "proxyUser"
password = "pwd"
}
}
inboundConfig : {
listeningAddress = "0.0.0.0:10005"
}
haConfig : {
haConnectionString = "zk://localhost:11105"
}
networkParametersPath = network-parameters

View File

@ -34,7 +34,41 @@ processResources {
}
dependencies {
compile project(':node-api')
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
compile(project(':core')) {
transitive = false // we control dependencies directly as the bridge is likely to be audited
}
compile(project(':node-api')) {
transitive = false// we control dependencies directly as the bridge is likely to be audited
}
// Here we pull in dependencies that would normally be pulled in transitively from :core and :node-api, but we need more fine grained control
// For AMQP serialisation.
compile "org.apache.qpid:proton-j:${protonj_version}"
// RxJava: observable streams of events.
compile "io.reactivex:rxjava:$rxjava_version"
compile("org.apache.activemq:artemis-core-client:${artemis_version}")
compile "org.apache.activemq:artemis-commons:${artemis_version}"
// Netty: All of it! Dn't depend upon ActiveMQ to pull it in correctly
compile "io.netty:netty-all:$netty_version"
// TypeSafe Config: for simple and human friendly config files.
compile "com.typesafe:config:$typesafe_config_version"
// The following dependencies are required to load and deserialize network-info, or other static initializers that get triggered.
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/ for deserializing our public keys and certs.
compile "net.i2p.crypto:eddsa:$eddsa_version"
// Bouncy castle support needed for X509 certificate manipulation
compile "org.bouncycastle:bcprov-jdk15on:${bouncycastle_version}"
compile "org.bouncycastle:bcpkix-jdk15on:${bouncycastle_version}"
// Seems to be needed?
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
// Pulled in by whitelist
compile "com.esotericsoftware:kryo:4.0.0"
// Log4J: logging framework (with SLF4J bindings)
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
@ -50,6 +84,7 @@ dependencies {
integrationTestCompile project(':node-driver')
integrationTestCompile "org.apache.curator:curator-test:${curator_version}"
testCompile "junit:junit:$junit_version"
testCompile "org.apache.curator:curator-test:${curator_version}"
testCompile project(':test-utils')
}

View File

@ -93,6 +93,7 @@ buildscript {
ext.eaagentloader_version = '1.0.3'
ext.curator_version = '4.0.0'
ext.jsch_version = '0.1.54'
ext.protonj_version = '0.27.1'
// Update 121 is required for ObjectInputFilter and at time of writing 131 was latest:
ext.java8_minUpdateVersion = '131'

View File

@ -44,7 +44,7 @@ dependencies {
compile "de.javakaffee:kryo-serializers:0.41"
// For AMQP serialisation.
compile "org.apache.qpid:proton-j:0.27.1"
compile "org.apache.qpid:proton-j:${protonj_version}"
// SQL connection pooling library
compile "com.zaxxer:HikariCP:$hikari_version"