From df7b1bccd6264f4fca64d67ac7468ed418819aad Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Fri, 11 Sep 2020 16:50:12 +0100 Subject: [PATCH 01/16] NOTICK Do not use Security.addProvider(BouncyCastleProvider()) in tests (#6250) (#6700) Do not use Security.addProvider(BouncyCastleProvider()) in tests, to avoid disruptions of other tests. Co-authored-by: Denis Rekalov --- .../net/corda/coretests/crypto/X509NameConstraintsTest.kt | 4 ++-- .../net/corda/node/amqp/CertificateRevocationListNodeTests.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt index c355ef8567..d41870507c 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt @@ -12,7 +12,6 @@ import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.jce.provider.BouncyCastleProvider import org.junit.Test -import java.security.Security import java.security.UnrecoverableKeyException import java.security.cert.CertPathValidator import java.security.cert.CertPathValidatorException @@ -95,7 +94,8 @@ class X509NameConstraintsTest { @Test fun `x500 name with correct cn and extra attribute`() { - Security.addProvider(BouncyCastleProvider()) + // Do not use Security.addProvider(BouncyCastleProvider()) to avoid EdDSA signature disruption in other tests. + Crypto.findProvider(BouncyCastleProvider.PROVIDER_NAME) val acceptableNames = listOf("CN=Bank A TLS, UID=", "O=Bank A") .map { GeneralSubtree(GeneralName(X500Name(it))) }.toTypedArray() diff --git a/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt b/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt index 13d220cbfb..f850e0ffa0 100644 --- a/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/amqp/CertificateRevocationListNodeTests.kt @@ -54,7 +54,6 @@ import java.math.BigInteger import java.net.InetSocketAddress import java.security.KeyPair import java.security.PrivateKey -import java.security.Security import java.security.cert.X509CRL import java.security.cert.X509Certificate import java.util.* @@ -113,7 +112,8 @@ class CertificateRevocationListNodeTests { @Before fun setUp() { - Security.addProvider(BouncyCastleProvider()) + // Do not use Security.addProvider(BouncyCastleProvider()) to avoid EdDSA signature disruption in other tests. + Crypto.findProvider(BouncyCastleProvider.PROVIDER_NAME) revokedNodeCerts.clear() server = CrlServer(NetworkHostAndPort("localhost", 0)) server.start() From ba802c6619cb6981c5cf15ce27c0811064e96b57 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Sat, 12 Sep 2020 23:52:52 +0100 Subject: [PATCH 02/16] CORDA-4028 Forcibly register security providers (#6702) Forcibly register security providers before starting Jimfs, to resolve a sequencing problem where Jimfs triggers loading of the SFTP filesystem provider, which in turn registers the standard BouncyCastle provider rather than the patched version Corda needs. --- .../corda/coretests/indentity/PartyAndCertificateTest.kt | 9 +++++++++ .../corda/node/services/network/NetworkMapUpdaterTest.kt | 4 ++++ .../node/services/network/NetworkParametersReaderTest.kt | 9 ++++++++- .../corda/node/services/network/NodeInfoWatcherTest.kt | 5 +++++ .../services/persistence/NodeAttachmentServiceTest.kt | 5 +++++ .../registration/NetworkRegistrationHelperTest.kt | 8 +++++++- 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt index e00693f6cc..ce337d4e58 100644 --- a/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt +++ b/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt @@ -2,6 +2,7 @@ package net.corda.coretests.indentity import com.google.common.jimfs.Configuration.unix import com.google.common.jimfs.Jimfs +import net.corda.core.crypto.Crypto import net.corda.core.crypto.entropyToKeyPair import net.corda.core.identity.CordaX500Name import net.corda.core.identity.Party @@ -14,6 +15,7 @@ import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.getTestPartyAndCertificate import net.corda.testing.internal.DEV_ROOT_CA import org.assertj.core.api.Assertions.assertThat +import org.junit.Before import org.junit.Rule import org.junit.Test import java.math.BigInteger @@ -24,6 +26,13 @@ class PartyAndCertificateTest { @JvmField val testSerialization = SerializationEnvironmentRule() + @Before + fun setUp() { + // Register providers before creating Jimfs filesystem. JimFs creates an SSHD instance which + // register BouncyCastle and EdDSA provider separately, which wrecks havoc. + Crypto.registerProviders() + } + @Test fun `reject a path with no roles`() { val path = X509Utilities.buildCertPath(DEV_ROOT_CA.certificate) diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt index 3842c044bf..83ec4a6369 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt @@ -76,6 +76,10 @@ class NetworkMapUpdaterTest { @Before fun setUp() { + // Register providers before creating Jimfs filesystem. JimFs creates an SSHD instance which + // register BouncyCastle and EdDSA provider separately, which wrecks havoc. + Crypto.registerProviders() + ourKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) ourNodeInfo = createNodeInfoAndSigned("Our info", ourKeyPair).signed server = NetworkMapServer(cacheExpiryMs.millis) diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt index 4378ddc314..f03e3a71a6 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt @@ -2,6 +2,7 @@ package net.corda.node.services.network import com.google.common.jimfs.Configuration import com.google.common.jimfs.Jimfs +import net.corda.core.crypto.Crypto import net.corda.core.internal.* import net.corda.core.serialization.deserialize import net.corda.core.utilities.days @@ -20,6 +21,7 @@ import org.junit.Rule import org.junit.Test import java.net.URL import java.nio.file.FileSystem +import java.security.Security import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotNull @@ -29,7 +31,7 @@ class NetworkParametersReaderTest { @JvmField val testSerialization = SerializationEnvironmentRule(true) - private val fs: FileSystem = Jimfs.newFileSystem(Configuration.unix()) + private lateinit var fs: FileSystem private val cacheTimeout = 100000.seconds private lateinit var server: NetworkMapServer @@ -37,6 +39,11 @@ class NetworkParametersReaderTest { @Before fun setUp() { + // Register providers before creating Jimfs filesystem. JimFs creates an SSHD instance which + // register BouncyCastle and EdDSA provider separately, which wrecks havoc. + Crypto.registerProviders() + + fs = Jimfs.newFileSystem(Configuration.unix()) server = NetworkMapServer(cacheTimeout) val address = server.start() networkMapClient = NetworkMapClient(URL("http://$address"), VersionInfo(1, "TEST", "TEST", "TEST")) diff --git a/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt index dd387c5a5c..83417c8f1d 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NodeInfoWatcherTest.kt @@ -2,6 +2,7 @@ package net.corda.node.services.network import com.google.common.jimfs.Configuration import com.google.common.jimfs.Jimfs +import net.corda.core.crypto.Crypto import net.corda.core.internal.NODE_INFO_DIRECTORY import net.corda.core.internal.createDirectories import net.corda.core.internal.div @@ -48,6 +49,10 @@ class NodeInfoWatcherTest { @Before fun start() { + // Register providers before creating Jimfs filesystem. JimFs creates an SSHD instance which + // register BouncyCastle and EdDSA provider separately, which wrecks havoc. + Crypto.registerProviders() + nodeInfoAndSigned = createNodeInfoAndSigned(ALICE_NAME) val identityService = makeTestIdentityService() keyManagementService = MockKeyManagementService(identityService) diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt index ad84218294..c79f4a76cd 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt @@ -7,6 +7,7 @@ import com.google.common.jimfs.Jimfs import com.nhaarman.mockito_kotlin.doReturn import com.nhaarman.mockito_kotlin.whenever import net.corda.core.contracts.ContractAttachment +import net.corda.core.crypto.Crypto import net.corda.core.crypto.SecureHash import net.corda.core.crypto.sha256 import net.corda.core.flows.FlowLogic @@ -68,6 +69,10 @@ class NodeAttachmentServiceTest { @Before fun setUp() { + // Register providers before creating Jimfs filesystem. JimFs creates an SSHD instance which + // register BouncyCastle and EdDSA provider separately, which wrecks havoc. + Crypto.registerProviders() + LogHelper.setLevel(PersistentUniquenessProvider::class) val dataSourceProperties = makeTestDataSourceProperties() diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index b337f8b18b..4992f0c381 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -36,6 +36,7 @@ import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.junit.After import org.junit.Before import org.junit.Test +import java.nio.file.FileSystem import java.security.PublicKey import java.security.cert.CertPathValidatorException import java.security.cert.X509Certificate @@ -45,7 +46,7 @@ import kotlin.test.assertFalse import kotlin.test.assertTrue class NetworkRegistrationHelperTest { - private val fs = Jimfs.newFileSystem(unix()) + private lateinit var fs: FileSystem private val nodeLegalName = ALICE_NAME private lateinit var config: NodeConfiguration @@ -54,6 +55,11 @@ class NetworkRegistrationHelperTest { @Before fun init() { + // Register providers before creating Jimfs filesystem. JimFs creates an SSHD instance which + // register BouncyCastle and EdDSA provider separately, which wrecks havoc. + Crypto.registerProviders() + + fs = Jimfs.newFileSystem(unix()) val baseDirectory = fs.getPath("/baseDir").createDirectories() abstract class AbstractNodeConfiguration : NodeConfiguration From b3d9d1291f74e0b3cc4be99425f87ac8fe18f789 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Mon, 14 Sep 2020 11:29:42 +0100 Subject: [PATCH 03/16] INFRA-683 Move Corda OS release branch builds to serial (#6703) Co-authored-by: Waldemar Zurowski --- .ci/dev/regression/Jenkinsfile | 273 ++++++++++++++---- build.gradle | 2 +- .../services/network/NetworkMapUpdaterTest.kt | 15 +- .../persistence/NodeAttachmentServiceTest.kt | 2 +- 4 files changed, 227 insertions(+), 65 deletions(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 41cc2ad218..3b131da9d2 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -1,58 +1,209 @@ -@Library('corda-shared-build-pipeline-steps') -import static com.r3.build.BuildControl.killAllExistingBuildsForJob +#!groovy +/** + * Jenkins pipeline to build Corda OS release branches and tags. + * PLEASE NOTE: we DO want to run a build for each commit!!! + */ -killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) +/** + * Sense environment + */ +boolean isReleaseTag = (env.TAG_NAME =~ /^release-.*(?version-properties" + /* every build related to Corda X.Y (GA, RC, HC, patch or snapshot) uses the same NexusIQ application */ + def version = sh (returnStdout: true, script: "grep ^version: version-properties | sed -e 's/^version: \\([0-9]\\+\\.[0-9]\\+\\).*\$/\\1/'").trim() + def groupId = sh (returnStdout: true, script: "grep ^group: version-properties | sed -e 's/^group: //'").trim() + def artifactId = 'corda' + nexusAppId = "${groupId}-${artifactId}-${version}" } - sh "kubectl auth can-i get pods" + nexusPolicyEvaluation ( + failBuildOnNetworkError: false, + iqApplication: selectedApplication(nexusAppId), // application *has* to exist before a build starts! + iqScanPatterns: [[scanPattern: 'node/capsule/build/libs/corda*.jar']], + iqStage: params.nexusIqStage + ) } } - stage('Regression Test') { + stage('Unit Test') { steps { - sh "./gradlew " + - "-DbuildId=\"\${BUILD_ID}\" " + - "-Dkubenetize=true " + - "-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " + - "-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " + - "-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " + - "-Dgit.branch=\"\${GIT_BRANCH}\" " + - "-Dgit.target.branch=\"\${GIT_BRANCH}\" " + - " parallelRegressionTest --stacktrace" + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'test' + ].join(' ') + } + } + + stage('Integration Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'integrationTest' + ].join(' ') + } + } + + stage('Smoke Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'smokeTest' + ].join(' ') + } + } + + stage('Slow Integration Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'slowIntegrationTest' + ].join(' ') + } + } + + stage('Deploy Node') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'deployNode' + ].join(' ') + } + } + + stage('Publish to Artifactory') { + when { + expression { isReleaseTag } + } + steps { + rtServer( + id: 'R3-Artifactory', + url: 'https://software.r3.com/artifactory', + credentialsId: 'artifactory-credentials' + ) + rtGradleDeployer( + id: 'deployer', + serverId: 'R3-Artifactory', + repo: 'corda-releases' + ) + rtGradleRun( + usesPlugin: true, + useWrapper: true, + switches: '-s --info', + tasks: 'artifactoryPublish', + deployerId: 'deployer', + buildName: env.ARTIFACTORY_BUILD_NAME + ) + rtPublishBuildInfo( + serverId: 'R3-Artifactory', + buildName: env.ARTIFACTORY_BUILD_NAME + ) + } + } + + stage('Publish Release to Docker Hub') { + when { + expression { !isInternalRelease && isReleaseTag } + } + steps { + withCredentials([ + usernamePassword(credentialsId: 'corda-publisher-docker-hub-credentials', + usernameVariable: 'DOCKER_USERNAME', + passwordVariable: 'DOCKER_PASSWORD')]) { + sh "./gradlew pushOfficialImages" + } } } } - post { always { - archiveArtifacts artifacts: '**/pod-logs/**/*.log', fingerprint: false - junit '**/build/test-results-xml/**/*.xml' + archiveArtifacts artifacts: '**/*.log', fingerprint: false + archiveArtifacts artifacts: '**/build/reports/tests/**', fingerprint: false + junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true script { try { @@ -84,38 +235,42 @@ pipeline { } } } - + script { - // We want to send a summary email, but want to limit to once per day. - // Comparing the dates of the previous and current builds achieves this, - // i.e. we will only send an email for the first build on a given day. - def prevBuildDate = new Date( - currentBuild?.previousBuild.timeInMillis ?: 0).clearTime() - def currentBuildDate = new Date( - currentBuild.timeInMillis).clearTime() + if (!isReleaseTag) { + // We want to send a summary email, but want to limit to once per day. + // Comparing the dates of the previous and current builds achieves this, + // i.e. we will only send an email for the first build on a given day. + def prevBuildDate = new Date( + currentBuild?.previousBuild.timeInMillis ?: 0).clearTime() + def currentBuildDate = new Date( + currentBuild.timeInMillis).clearTime() - if (prevBuildDate != currentBuildDate) { - def statusSymbol = '\u2753' - switch(currentBuild.result) { - case 'SUCCESS': - statusSymbol = '\u2705' - break; - case 'UNSTABLE': - case 'FAILURE': - statusSymbol = '\u274c' - break; - default: - break; + if (prevBuildDate != currentBuildDate) { + def statusSymbol = '\u2753' + switch(currentBuild.result) { + case 'SUCCESS': + statusSymbol = '\u2705' + break; + case 'UNSTABLE': + statusSymbol = '\u26A0' + break; + case 'FAILURE': + statusSymbol = '\u274c' + break; + default: + break; + } + + echo('First build for this date, sending summary email') + emailext to: '$DEFAULT_RECIPIENTS', + subject: "$statusSymbol" + '$BRANCH_NAME regression tests - $BUILD_STATUS', + mimeType: 'text/html', + body: '${SCRIPT, template="groovy-html.template"}' + } else { + echo('Already sent summary email today, suppressing') } - - echo('First build for this date, sending summary email') - emailext to: '$DEFAULT_RECIPIENTS', - subject: "$statusSymbol" + '$BRANCH_NAME regression tests - $BUILD_STATUS', - mimeType: 'text/html', - body: '${SCRIPT, template="groovy-html.template"}' - } else { - echo('Already sent summary email today, suppressing') } } } diff --git a/build.gradle b/build.gradle index e1b3594f59..2a33ecb311 100644 --- a/build.gradle +++ b/build.gradle @@ -309,7 +309,7 @@ allprojects { } tasks.withType(Test) { - forkEvery = 10 + forkEvery = 20 ignoreFailures = project.hasProperty('tests.ignoreFailures') ? project.property('tests.ignoreFailures').toBoolean() : false failFast = project.hasProperty('tests.failFast') ? project.property('tests.failFast').toBoolean() : false diff --git a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt index 83ec4a6369..adec0f92d7 100644 --- a/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/network/NetworkMapUpdaterTest.kt @@ -43,6 +43,8 @@ import org.junit.* import rx.schedulers.TestScheduler import java.io.IOException import java.net.URL +import java.nio.file.FileSystem +import java.nio.file.Path import java.security.KeyPair import java.time.Instant import java.time.temporal.ChronoUnit @@ -60,11 +62,12 @@ class NetworkMapUpdaterTest { private val cacheExpiryMs = 1000 private val privateNetUUID = UUID.randomUUID() - private val fs = Jimfs.newFileSystem(unix()) - private val baseDir = fs.getPath("/node") - private val nodeInfoDir = baseDir / NODE_INFO_DIRECTORY + private lateinit var fs: FileSystem + private lateinit var baseDir: Path + private val nodeInfoDir + get() = baseDir / NODE_INFO_DIRECTORY private val scheduler = TestScheduler() - private val fileWatcher = NodeInfoWatcher(baseDir, scheduler) + private lateinit var fileWatcher: NodeInfoWatcher private val nodeReadyFuture = openFuture() private val networkMapCache = createMockNetworkMapCache() private lateinit var ourKeyPair: KeyPair @@ -80,6 +83,10 @@ class NetworkMapUpdaterTest { // register BouncyCastle and EdDSA provider separately, which wrecks havoc. Crypto.registerProviders() + fs = Jimfs.newFileSystem(unix()) + baseDir = fs.getPath("/node") + fileWatcher = NodeInfoWatcher(baseDir, scheduler) + ourKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) ourNodeInfo = createNodeInfoAndSigned("Our info", ourKeyPair).signed server = NetworkMapServer(cacheExpiryMs.millis) diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt index c79f4a76cd..e0f447ff62 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentServiceTest.kt @@ -25,7 +25,6 @@ import net.corda.nodeapi.exceptions.DuplicateAttachmentException import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.common.internal.testNetworkParameters -import net.corda.testing.core.internal.ContractJarTestUtils import net.corda.testing.core.internal.ContractJarTestUtils.makeTestContractJar import net.corda.testing.core.internal.ContractJarTestUtils.makeTestJar import net.corda.testing.core.internal.ContractJarTestUtils.makeTestSignedContractJar @@ -96,6 +95,7 @@ class NodeAttachmentServiceTest { @After fun tearDown() { database.close() + fs.close() } @Test From c0d354e97a05150f9a81c856aec65134563294e8 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Tue, 15 Sep 2020 11:59:46 +0100 Subject: [PATCH 04/16] CORDA-3845: Update BC, log4j, slf4j (#6699) * CORDA-3845: Update BC to 1.64 * CORDA-3845: Upgraded log4j to 2.12.1 * We can remove the use of Manifests from the logging package so that when _it_ logs it doesn't error on the fact the stream was already closed by the default Java logger. * Remove the logging package as a plugin * latest BC version * Remove old test * Fix some rebased changes to log file handling * Update slf4j too Co-authored-by: Ryan Fowler Co-authored-by: Adel El-Beik --- build.gradle | 4 +- .../common/logging/ErrorCodeRewritePolicy.kt | 28 --------- config/dev/log4j2.xml | 6 +- constants.properties | 4 +- .../node/logging/ErrorCodeLoggingTests.kt | 62 ------------------- .../kotlin/net/corda/testing/driver/Driver.kt | 3 + .../src/main/resources/log4j2-test.xml | 5 +- .../src/test/resources/log4j2-test.xml | 5 +- 8 files changed, 10 insertions(+), 107 deletions(-) delete mode 100644 common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt delete mode 100644 node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt diff --git a/build.gradle b/build.gradle index 2a33ecb311..14c1b970ee 100644 --- a/build.gradle +++ b/build.gradle @@ -67,8 +67,8 @@ buildscript { ext.jersey_version = '2.25' ext.servlet_version = '4.0.1' ext.assertj_version = '3.12.2' - ext.slf4j_version = '1.7.26' - ext.log4j_version = '2.11.2' + ext.slf4j_version = '1.7.30' + ext.log4j_version = '2.12.1' ext.bouncycastle_version = constants.getProperty("bouncycastleVersion") ext.guava_version = constants.getProperty("guavaVersion") ext.caffeine_version = constants.getProperty("caffeineVersion") diff --git a/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt b/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt deleted file mode 100644 index 5f0e24bef9..0000000000 --- a/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.corda.common.logging - -import org.apache.logging.log4j.core.Core -import org.apache.logging.log4j.core.LogEvent -import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy -import org.apache.logging.log4j.core.config.plugins.Plugin -import org.apache.logging.log4j.core.config.plugins.PluginFactory -import org.apache.logging.log4j.core.impl.Log4jLogEvent - -@Plugin(name = "ErrorCodeRewritePolicy", category = Core.CATEGORY_NAME, elementType = "rewritePolicy", printObject = false) -class ErrorCodeRewritePolicy : RewritePolicy { - override fun rewrite(source: LogEvent): LogEvent? { - val newMessage = source.message?.withErrorCodeFor(source.thrown, source.level) - return if (newMessage == source.message) { - source - } else { - Log4jLogEvent.Builder(source).setMessage(newMessage).build() - } - } - - companion object { - @JvmStatic - @PluginFactory - fun createPolicy(): ErrorCodeRewritePolicy { - return ErrorCodeRewritePolicy() - } - } -} \ No newline at end of file diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml index fe67301f84..6593f84ae8 100644 --- a/config/dev/log4j2.xml +++ b/config/dev/log4j2.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} @@ -172,21 +172,17 @@ - - - - diff --git a/constants.properties b/constants.properties index 6cf6942c4d..362ac6b643 100644 --- a/constants.properties +++ b/constants.properties @@ -22,8 +22,8 @@ quasarVersion11=0.8.0 # Specify a classifier for Java 11 built artifacts jdkClassifier11=jdk11 proguardVersion=6.1.1 -bouncycastleVersion=1.61 -classgraphVersion=4.8.41 +bouncycastleVersion=1.66 +classgraphVersion=4.8.78 disruptorVersion=3.4.2 typesafeConfigVersion=1.3.4 jsr305Version=3.0.2 diff --git a/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt b/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt deleted file mode 100644 index e0532911f0..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt +++ /dev/null @@ -1,62 +0,0 @@ -package net.corda.node.logging - -import net.corda.core.flows.FlowException -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.StartableByRPC -import net.corda.core.internal.div -import net.corda.core.messaging.FlowHandle -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.getOrThrow -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.NodeHandle -import net.corda.testing.driver.driver -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import java.io.File - -class ErrorCodeLoggingTests { - @Test - fun `log entries with a throwable and ERROR or WARN get an error code appended`() { - driver(DriverParameters(notarySpecs = emptyList())) { - val node = startNode(startInSameProcess = false).getOrThrow() - node.rpc.startFlow(::MyFlow).waitForCompletion() - val logFile = node.logFile() - - val linesWithErrorCode = logFile.useLines { lines -> lines.filter { line -> line.contains("[errorCode=") }.filter { line -> line.contains("moreInformationAt=https://errors.corda.net/") }.toList() } - - assertThat(linesWithErrorCode).isNotEmpty - } - } - - // This is used to detect broken logging which can be caused by loggers being initialized - // before the initLogging() call is made - @Test - fun `When logging is set to error level, there are no other levels logged after node startup`() { - driver(DriverParameters(notarySpecs = emptyList())) { - val node = startNode(startInSameProcess = false, logLevelOverride = "ERROR").getOrThrow() - node.rpc.startFlow(::MyFlow).waitForCompletion() - val logFile = node.logFile() - // An exception thrown in a flow will log at the "INFO" level. - assertThat(logFile.length()).isEqualTo(0) - } - } - - @StartableByRPC - @InitiatingFlow - class MyFlow : FlowLogic() { - override fun call(): String { - throw IllegalArgumentException("Mwahahahah") - } - } -} - -private fun FlowHandle<*>.waitForCompletion() { - try { - returnValue.getOrThrow() - } catch (e: Exception) { - // This is expected to throw an exception, using getOrThrow() just to wait until done. - } -} - -fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index a5085085d4..33953c76df 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -26,6 +26,7 @@ import net.corda.testing.node.internal.genericDriver import net.corda.testing.node.internal.getTimestampAsDirectoryName import net.corda.testing.node.internal.newContext import rx.Observable +import java.io.File import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.atomic.AtomicInteger @@ -66,6 +67,8 @@ interface NodeHandle : AutoCloseable { fun stop() } +fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() + /** Interface which represents an out of process node and exposes its process handle. **/ @DoNotImplement interface OutOfProcess : NodeHandle { diff --git a/testing/test-common/src/main/resources/log4j2-test.xml b/testing/test-common/src/main/resources/log4j2-test.xml index d8d489af1f..45910c8ca5 100644 --- a/testing/test-common/src/main/resources/log4j2-test.xml +++ b/testing/test-common/src/main/resources/log4j2-test.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} @@ -63,17 +63,14 @@ - - - diff --git a/testing/test-db/src/test/resources/log4j2-test.xml b/testing/test-db/src/test/resources/log4j2-test.xml index d0941363a9..35b51709ed 100644 --- a/testing/test-db/src/test/resources/log4j2-test.xml +++ b/testing/test-db/src/test/resources/log4j2-test.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} @@ -65,17 +65,14 @@ - - - From 80bac915ee27f1d4f0dc7a0d558fa4e9e43cd105 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Waldemar=20=C5=BBurowski?= <45210402+wzur-r3@users.noreply.github.com> Date: Tue, 15 Sep 2020 12:10:06 +0100 Subject: [PATCH 05/16] INFRA-683: manual sync with enterprise 43 (#6709) * Add support for handling release candidate for Docker publishing * Re-enabled Sonatype check * Artifactory build name includes branch / tag name * Environment variables sorted alphabetically --- .ci/dev/regression/Jenkinsfile | 35 +++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 3b131da9d2..388d64360c 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -9,11 +9,13 @@ */ boolean isReleaseTag = (env.TAG_NAME =~ /^release-.*(?version-properties" @@ -186,14 +186,19 @@ pipeline { stage('Publish Release to Docker Hub') { when { - expression { !isInternalRelease && isReleaseTag } + expression { isReleaseTag && !isInternalRelease && !isReleaseCandidate} } steps { withCredentials([ usernamePassword(credentialsId: 'corda-publisher-docker-hub-credentials', usernameVariable: 'DOCKER_USERNAME', - passwordVariable: 'DOCKER_PASSWORD')]) { - sh "./gradlew pushOfficialImages" + passwordVariable: 'DOCKER_PASSWORD') + ]) { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'pushOfficialImages' + ].join(' ') } } } @@ -225,8 +230,8 @@ pipeline { sourceCaptureExpression: '.*test-results-xml/.*-([\\d]+)/.*/([^/]+)$', targetNameExpression: '$1-$2')]) allure includeProperties: false, - jdk: '', - results: [[path: '**/allure-input']] + jdk: '', + results: [[path: '**/allure-input']] } catch (err) { echo("Allure report generation failed: $err") From bcfbe22d1af4505986bcc7a496f0c99826580e03 Mon Sep 17 00:00:00 2001 From: Waldemar Zurowski Date: Wed, 16 Sep 2020 10:22:36 +0200 Subject: [PATCH 06/16] Synchronised changes with OS 4.3 branch --- .ci/dev/regression/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 388d64360c..b805ff0fd8 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -248,7 +248,7 @@ pipeline { // Comparing the dates of the previous and current builds achieves this, // i.e. we will only send an email for the first build on a given day. def prevBuildDate = new Date( - currentBuild?.previousBuild.timeInMillis ?: 0).clearTime() + currentBuild.previousBuild?.timeInMillis ?: 0).clearTime() def currentBuildDate = new Date( currentBuild.timeInMillis).clearTime() From 0226f7ccb9b10283df2c5f3adb2755d38af66c9d Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Wed, 16 Sep 2020 13:10:54 +0100 Subject: [PATCH 07/16] CORDA-4034 Shut down node in AuthDBTests (#6714) --- node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt index a901718f05..164fe68523 100644 --- a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt @@ -215,6 +215,7 @@ class AuthDBTests : NodeBasedTest() { @After fun tearDown() { + node.node.stop() db.close() } From 6ab9557061fc338ee33b62be4d7bbbbe27cebde0 Mon Sep 17 00:00:00 2001 From: Waldemar Zurowski Date: Wed, 16 Sep 2020 09:52:41 +0200 Subject: [PATCH 08/16] Use safe navigation operator for correct field when checking the time of previous build. Handles an edge case when there is the very first build for the branch --- .ci/dev/regression/Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 388d64360c..b805ff0fd8 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -248,7 +248,7 @@ pipeline { // Comparing the dates of the previous and current builds achieves this, // i.e. we will only send an email for the first build on a given day. def prevBuildDate = new Date( - currentBuild?.previousBuild.timeInMillis ?: 0).clearTime() + currentBuild.previousBuild?.timeInMillis ?: 0).clearTime() def currentBuildDate = new Date( currentBuild.timeInMillis).clearTime() From cc14c8e53aac360314a9ae4be5bc274d003ef60d Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Wed, 16 Sep 2020 16:44:31 +0100 Subject: [PATCH 09/16] CORDA-4034 Rebuild AuthDBTests so H2 shuts down correctly (#6718) * CORDA-4034 Rebuild AuthDBTests so H2 shuts down correctly --- .../kotlin/net/corda/node/AuthDBTests.kt | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt index 164fe68523..448e306623 100644 --- a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt @@ -22,6 +22,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized +import java.sql.Connection import java.sql.Statement import java.util.* import javax.sql.DataSource @@ -33,7 +34,7 @@ import kotlin.test.assertFailsWith */ @RunWith(Parameterized::class) class AuthDBTests : NodeBasedTest() { - private lateinit var node: NodeWithInfo + private var node: NodeWithInfo? = null private lateinit var client: CordaRPCClient private lateinit var db: UsersDB @@ -93,8 +94,9 @@ class AuthDBTests : NodeBasedTest() { ) ) - node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig) - client = CordaRPCClient(node.node.configuration.rpcOptions.address) + node = startNode(ALICE_NAME, rpcUsers = emptyList(), configOverrides = securityConfig).also { node -> + client = CordaRPCClient(node.node.configuration.rpcOptions.address) + } } @Test @@ -215,7 +217,7 @@ class AuthDBTests : NodeBasedTest() { @After fun tearDown() { - node.node.stop() + node?.node?.stop() db.close() } @@ -229,7 +231,7 @@ private data class RoleAndPermissions(val role: String, val permissions: List = emptyList(), roleAndPermissions: List = emptyList()) : AutoCloseable { - val jdbcUrl = "jdbc:h2:mem:$name;DB_CLOSE_DELAY=-1" + val jdbcUrl = "jdbc:h2:mem:$name" companion object { const val DB_CREATE_SCHEMA = """ @@ -270,36 +272,34 @@ private class UsersDB(name: String, users: List = emptyList(), rol } } - private val dataSource: DataSource + private val connection: Connection private inline fun session(statement: (Statement) -> Unit) { - dataSource.connection.use { - it.autoCommit = false - it.createStatement().use(statement) - it.commit() - } + connection.createStatement().use(statement) + connection.commit() } init { - dataSource = DataSourceFactory.createDataSource(Properties().apply { + require(users.map { it.username }.toSet().size == users.size) { + "Duplicate username in input" + } + connection = DataSourceFactory.createDataSource(Properties().apply { put("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource") put("dataSource.url", jdbcUrl) }, false) + .connection + .apply { + autoCommit = false + } session { it.execute(DB_CREATE_SCHEMA) } - require(users.map { it.username }.toSet().size == users.size) { - "Duplicate username in input" - } users.forEach { insert(it) } roleAndPermissions.forEach { insert(it) } } override fun close() { - dataSource.connection.use { - it.createStatement().use { - it.execute("DROP ALL OBJECTS") - } - } + // Close the connection, at which point the database will shut down + connection.close() } } From 2064a572ba94c6777a4b9051218305def1b642b4 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Fri, 18 Sep 2020 00:23:16 +0100 Subject: [PATCH 10/16] CORDA-4034 ENT-5679 CORDA-3845 Correct instability of linear builds (#6722) * CORDA-4034 Reduce forkEvery to 15 to attempt to mitigate memory leak. * ENT-5679 Disable test which triggers OOM * Run tests on two Jenkins agents * Fixed processing JUnit test results by Allure * Add timeouts to VaultObserverExceptionTest * Revert "CORDA-3845: Update BC, log4j, slf4j (#6699)" to eliminate introduced memory leaks Co-authored-by: Waldemar Zurowski --- .ci/dev/regression/Jenkinsfile | 174 ++++++++++++------ build.gradle | 6 +- .../common/logging/ErrorCodeRewritePolicy.kt | 28 +++ config/dev/log4j2.xml | 6 +- constants.properties | 4 +- .../node/logging/ErrorCodeLoggingTests.kt | 62 +++++++ .../statemachine/LargeTransactionsTest.kt | 2 + .../vault/VaultObserverExceptionTest.kt | 36 ++-- .../kotlin/net/corda/testing/driver/Driver.kt | 3 - .../src/main/resources/log4j2-test.xml | 5 +- .../src/test/resources/log4j2-test.xml | 5 +- 11 files changed, 249 insertions(+), 82 deletions(-) create mode 100644 common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt create mode 100644 node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index b805ff0fd8..7efc7a3b72 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -52,10 +52,15 @@ String COMMON_GRADLE_PARAMS = [ pipeline { agent { label 'standard' } + + /* + * List options in alphabetical order + */ options { - timestamps() - timeout(time: 6, unit: 'HOURS') buildDiscarder(logRotator(daysToKeepStr: '14', artifactDaysToKeepStr: '14')) + parallelsAlwaysFailFast() + timeout(time: 6, unit: 'HOURS') + timestamps() } parameters { @@ -85,6 +90,11 @@ pipeline { } } + stage('Stash') { + steps { + stash name: 'compiled', useDefaultExcludes: false + } + } stage('Sonatype Check') { steps { script { @@ -104,53 +114,110 @@ pipeline { } } - stage('Unit Test') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'test' - ].join(' ') - } - } + stage('All Tests') { + parallel { + stage('Another agent') { + agent { + label 'standard' + } + options { + skipDefaultCheckout true + } + post { + always { + archiveArtifacts artifacts: '**/*.log', fingerprint: false + archiveArtifacts artifacts: '**/build/reports/tests/**', fingerprint: false + junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true + /* + * Copy all JUnit results files into a single top level directory. + * This is necessary to stop the allure plugin from hitting out + * of memory errors due to being passed many directories with + * long paths. + * + * File names are pre-pended with a prefix when + * copied to avoid collisions between files where the same test + * classes have run on multiple agents. + */ + fileOperations([fileCopyOperation( + includes: '**/build/test-results/**/*.xml', + targetLocation: 'allure-input', + flattenFiles: true, + renameFiles: true, + sourceCaptureExpression: '.*/([^/]+)$', + targetNameExpression: 'other-agent-$1')]) + stash name: 'allure-input', includes: 'allure-input/**', useDefaultExcludes: false + } + cleanup { + deleteDir() /* clean up our workspace */ + } + } + stages { + stage('Unstash') { + steps { + unstash 'compiled' + } + } + stage('Recompile') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'jar' + ].join(' ') + } + } + stage('Unit Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'test' + ].join(' ') + } + } + stage('Smoke Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'smokeTest' + ].join(' ') + } + } + stage('Slow Integration Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'slowIntegrationTest' + ].join(' ') + } + } + } + } + stage('Same agent') { + stages { + stage('Integration Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'integrationTest' + ].join(' ') + } + } - stage('Integration Test') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'integrationTest' - ].join(' ') - } - } - - stage('Smoke Test') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'smokeTest' - ].join(' ') - } - } - - stage('Slow Integration Test') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'slowIntegrationTest' - ].join(' ') - } - } - - stage('Deploy Node') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'deployNode' - ].join(' ') + stage('Deploy Node') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'deployNode' + ].join(' ') + } + } + } + } } } @@ -209,6 +276,7 @@ pipeline { archiveArtifacts artifacts: '**/*.log', fingerprint: false archiveArtifacts artifacts: '**/build/reports/tests/**', fingerprint: false junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true + unstash 'allure-input' script { try { @@ -218,17 +286,17 @@ pipeline { * of memory errors due to being passed many directories with * long paths. * - * File names are pre-pended with the pod number when + * File names are pre-pended with a prefix when * copied to avoid collisions between files where the same test - * classes have run on multiple pods. + * classes have run on multiple agents. */ fileOperations([fileCopyOperation( - includes: '**/test-results-xml/**/test-runs/test-reports/**', + includes: '**/build/test-results/**/*.xml', targetLocation: 'allure-input', flattenFiles: true, renameFiles: true, - sourceCaptureExpression: '.*test-results-xml/.*-([\\d]+)/.*/([^/]+)$', - targetNameExpression: '$1-$2')]) + sourceCaptureExpression: '.*/([^/]+)$', + targetNameExpression: 'same-agent-$1')]) allure includeProperties: false, jdk: '', results: [[path: '**/allure-input']] diff --git a/build.gradle b/build.gradle index 14c1b970ee..bc5933c7de 100644 --- a/build.gradle +++ b/build.gradle @@ -67,8 +67,8 @@ buildscript { ext.jersey_version = '2.25' ext.servlet_version = '4.0.1' ext.assertj_version = '3.12.2' - ext.slf4j_version = '1.7.30' - ext.log4j_version = '2.12.1' + ext.slf4j_version = '1.7.26' + ext.log4j_version = '2.11.2' ext.bouncycastle_version = constants.getProperty("bouncycastleVersion") ext.guava_version = constants.getProperty("guavaVersion") ext.caffeine_version = constants.getProperty("caffeineVersion") @@ -309,7 +309,7 @@ allprojects { } tasks.withType(Test) { - forkEvery = 20 + forkEvery = 15 ignoreFailures = project.hasProperty('tests.ignoreFailures') ? project.property('tests.ignoreFailures').toBoolean() : false failFast = project.hasProperty('tests.failFast') ? project.property('tests.failFast').toBoolean() : false diff --git a/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt b/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt new file mode 100644 index 0000000000..5f0e24bef9 --- /dev/null +++ b/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt @@ -0,0 +1,28 @@ +package net.corda.common.logging + +import org.apache.logging.log4j.core.Core +import org.apache.logging.log4j.core.LogEvent +import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy +import org.apache.logging.log4j.core.config.plugins.Plugin +import org.apache.logging.log4j.core.config.plugins.PluginFactory +import org.apache.logging.log4j.core.impl.Log4jLogEvent + +@Plugin(name = "ErrorCodeRewritePolicy", category = Core.CATEGORY_NAME, elementType = "rewritePolicy", printObject = false) +class ErrorCodeRewritePolicy : RewritePolicy { + override fun rewrite(source: LogEvent): LogEvent? { + val newMessage = source.message?.withErrorCodeFor(source.thrown, source.level) + return if (newMessage == source.message) { + source + } else { + Log4jLogEvent.Builder(source).setMessage(newMessage).build() + } + } + + companion object { + @JvmStatic + @PluginFactory + fun createPolicy(): ErrorCodeRewritePolicy { + return ErrorCodeRewritePolicy() + } + } +} \ No newline at end of file diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml index 6593f84ae8..fe67301f84 100644 --- a/config/dev/log4j2.xml +++ b/config/dev/log4j2.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} @@ -172,17 +172,21 @@ + + + + diff --git a/constants.properties b/constants.properties index 362ac6b643..6cf6942c4d 100644 --- a/constants.properties +++ b/constants.properties @@ -22,8 +22,8 @@ quasarVersion11=0.8.0 # Specify a classifier for Java 11 built artifacts jdkClassifier11=jdk11 proguardVersion=6.1.1 -bouncycastleVersion=1.66 -classgraphVersion=4.8.78 +bouncycastleVersion=1.61 +classgraphVersion=4.8.41 disruptorVersion=3.4.2 typesafeConfigVersion=1.3.4 jsr305Version=3.0.2 diff --git a/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt b/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt new file mode 100644 index 0000000000..e0532911f0 --- /dev/null +++ b/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt @@ -0,0 +1,62 @@ +package net.corda.node.logging + +import net.corda.core.flows.FlowException +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.StartableByRPC +import net.corda.core.internal.div +import net.corda.core.messaging.FlowHandle +import net.corda.core.messaging.startFlow +import net.corda.core.utilities.getOrThrow +import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.NodeHandle +import net.corda.testing.driver.driver +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test +import java.io.File + +class ErrorCodeLoggingTests { + @Test + fun `log entries with a throwable and ERROR or WARN get an error code appended`() { + driver(DriverParameters(notarySpecs = emptyList())) { + val node = startNode(startInSameProcess = false).getOrThrow() + node.rpc.startFlow(::MyFlow).waitForCompletion() + val logFile = node.logFile() + + val linesWithErrorCode = logFile.useLines { lines -> lines.filter { line -> line.contains("[errorCode=") }.filter { line -> line.contains("moreInformationAt=https://errors.corda.net/") }.toList() } + + assertThat(linesWithErrorCode).isNotEmpty + } + } + + // This is used to detect broken logging which can be caused by loggers being initialized + // before the initLogging() call is made + @Test + fun `When logging is set to error level, there are no other levels logged after node startup`() { + driver(DriverParameters(notarySpecs = emptyList())) { + val node = startNode(startInSameProcess = false, logLevelOverride = "ERROR").getOrThrow() + node.rpc.startFlow(::MyFlow).waitForCompletion() + val logFile = node.logFile() + // An exception thrown in a flow will log at the "INFO" level. + assertThat(logFile.length()).isEqualTo(0) + } + } + + @StartableByRPC + @InitiatingFlow + class MyFlow : FlowLogic() { + override fun call(): String { + throw IllegalArgumentException("Mwahahahah") + } + } +} + +private fun FlowHandle<*>.waitForCompletion() { + try { + returnValue.getOrThrow() + } catch (e: Exception) { + // This is expected to throw an exception, using getOrThrow() just to wait until done. + } +} + +fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() \ No newline at end of file diff --git a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt index 532b6ca141..3e723c223d 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/statemachine/LargeTransactionsTest.kt @@ -22,6 +22,7 @@ import net.corda.testing.driver.driver import net.corda.testing.node.User import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP import net.corda.testing.node.internal.enclosedCordapp +import org.junit.Ignore import org.junit.Test import kotlin.test.assertEquals @@ -29,6 +30,7 @@ import kotlin.test.assertEquals * Check that we can add lots of large attachments to a transaction and that it works OK, e.g. does not hit the * transaction size limit (which should only consider the hashes). */ +@Ignore("ENT-5679: This test triggers OOM errors") class LargeTransactionsTest { private companion object { val BOB = TestIdentity(BOB_NAME, 80).party diff --git a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt index 06c3bc1acf..8b71e344e0 100644 --- a/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/services/vault/VaultObserverExceptionTest.kt @@ -29,7 +29,7 @@ import kotlin.test.assertFailsWith class VaultObserverExceptionTest { companion object { - + val waitForFlowDuration = 45.seconds val log = contextLogger() private fun testCordapps() = listOf( @@ -49,7 +49,7 @@ class VaultObserverExceptionTest { * Causing an SqlException via a syntax error in a vault observer causes the flow to hit the * DatabsaseEndocrinologist in the FlowHospital and being kept for overnight observation */ - @Test + @Test(timeout=300_000) fun unhandledSqlExceptionFromVaultObserverGetsHospitatlised() { val testControlFuture = openFuture().toCompletableFuture() @@ -73,7 +73,7 @@ class VaultObserverExceptionTest { "Syntax Error in Custom SQL", CreateStateFlow.errorTargetsToNum(CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError) ).returnValue.then { testControlFuture.complete(false) } - val foundExpectedException = testControlFuture.getOrThrow(30.seconds) + val foundExpectedException = testControlFuture.getOrThrow(waitForFlowDuration) Assert.assertTrue(foundExpectedException) } @@ -83,7 +83,7 @@ class VaultObserverExceptionTest { * Throwing a random (non-SQL releated) exception from a vault observer causes the flow to be * aborted when unhandled in user code */ - @Test + @Test(timeout=300_000) fun otherExceptionsFromVaultObserverBringFlowDown() { driver(DriverParameters( startNodesInProcess = true, @@ -95,7 +95,7 @@ class VaultObserverExceptionTest { ::Initiator, "InvalidParameterException", CreateStateFlow.errorTargetsToNum(CreateStateFlow.ErrorTarget.ServiceThrowInvalidParameter) - ).returnValue.getOrThrow(30.seconds) + ).returnValue.getOrThrow(waitForFlowDuration) } } } @@ -104,7 +104,7 @@ class VaultObserverExceptionTest { * A random exception from a VaultObserver will bring the Rx Observer down, but can be handled in the flow * triggering the observer, and the flow will continue successfully (for some values of success) */ - @Test + @Test(timeout=300_000) fun otherExceptionsFromVaultObserverCanBeSuppressedInFlow() { driver(DriverParameters( startNodesInProcess = true, @@ -114,7 +114,7 @@ class VaultObserverExceptionTest { aliceNode.rpc.startFlow(::Initiator, "InvalidParameterException", CreateStateFlow.errorTargetsToNum( CreateStateFlow.ErrorTarget.ServiceThrowInvalidParameter, CreateStateFlow.ErrorTarget.FlowSwallowErrors)) - .returnValue.getOrThrow(30.seconds) + .returnValue.getOrThrow(waitForFlowDuration) } } @@ -123,7 +123,7 @@ class VaultObserverExceptionTest { * If the state we are trying to persist triggers a persistence exception, the flow hospital will retry the flow * and keep it in for observation if errors persist. */ - @Test + @Test(timeout=300_000) fun persistenceExceptionOnCommitGetsRetriedAndThenGetsKeptForObservation() { var admitted = 0 var observation = 0 @@ -152,7 +152,7 @@ class VaultObserverExceptionTest { * If we have a state causing a database error lined up for persistence, calling jdbConnection() in * the vault observer will trigger a flush that throws. This will be kept in for observation. */ - @Test + @Test(timeout=300_000) fun persistenceExceptionOnFlushGetsRetriedAndThenGetsKeptForObservation() { var counter = 0 StaffedFlowHospital.DatabaseEndocrinologist.customConditions.add { @@ -179,7 +179,7 @@ class VaultObserverExceptionTest { aliceNode.rpc.startFlow(::Initiator, "EntityManager", errorTargetsToNum( CreateStateFlow.ErrorTarget.ServiceValidUpdate, CreateStateFlow.ErrorTarget.TxInvalidState)) - .returnValue.getOrThrow(30.seconds) + .returnValue.getOrThrow(waitForFlowDuration) } } Assert.assertTrue("Flow has not been to hospital", counter > 0) @@ -193,7 +193,7 @@ class VaultObserverExceptionTest { * does not change the outcome - the first exception in the service will bring the service down and will * be caught by the flow, but the state machine will error the flow anyway as Corda code threw. */ - @Test + @Test(timeout=300_000) fun persistenceExceptionOnFlushInVaultObserverCannotBeSuppressedInFlow() { var counter = 0 StaffedFlowHospital.DatabaseEndocrinologist.customConditions.add { @@ -220,7 +220,7 @@ class VaultObserverExceptionTest { CreateStateFlow.ErrorTarget.TxInvalidState, CreateStateFlow.ErrorTarget.FlowSwallowErrors)) val flowResult = flowHandle.returnValue - assertFailsWith("PersistenceException") { flowResult.getOrThrow(30.seconds) } + assertFailsWith("PersistenceException") { flowResult.getOrThrow(waitForFlowDuration) } Assert.assertTrue("Flow has not been to hospital", counter > 0) } } @@ -231,7 +231,7 @@ class VaultObserverExceptionTest { * Trying to catch and suppress that exception inside the service does protect the service, but the new * interceptor will fail the flow anyway. The flow will be kept in for observation if errors persist. */ - @Test + @Test(timeout=300_000) fun persistenceExceptionOnFlushInVaultObserverCannotBeSuppressedInService() { var counter = 0 StaffedFlowHospital.DatabaseEndocrinologist.customConditions.add { @@ -257,7 +257,7 @@ class VaultObserverExceptionTest { CreateStateFlow.ErrorTarget.TxInvalidState, CreateStateFlow.ErrorTarget.ServiceSwallowErrors)) val flowResult = flowHandle.returnValue - assertFailsWith("PersistenceException") { flowResult.getOrThrow(30.seconds) } + assertFailsWith("PersistenceException") { flowResult.getOrThrow(waitForFlowDuration) } Assert.assertTrue("Flow has not been to hospital", counter > 0) } } @@ -267,7 +267,7 @@ class VaultObserverExceptionTest { * therefore handling it in flow code is no good, and the error will be passed to the flow hospital via the * interceptor. */ - @Test + @Test(timeout=300_000) fun syntaxErrorInUserCodeInServiceCannotBeSuppressedInFlow() { val testControlFuture = openFuture() StaffedFlowHospital.onFlowKeptForOvernightObservation.add { _, _ -> @@ -288,7 +288,7 @@ class VaultObserverExceptionTest { log.info("Flow has finished") testControlFuture.set(false) } - Assert.assertTrue("Flow has not been kept in hospital", testControlFuture.getOrThrow(30.seconds)) + Assert.assertTrue("Flow has not been kept in hospital", testControlFuture.getOrThrow(waitForFlowDuration)) } } @@ -296,7 +296,7 @@ class VaultObserverExceptionTest { * User code throwing a syntax error and catching suppressing that within the observer code is fine * and should not have any impact on the rest of the flow */ - @Test + @Test(timeout=300_000) fun syntaxErrorInUserCodeInServiceCanBeSuppressedInService() { driver(DriverParameters( startNodesInProcess = true, @@ -307,7 +307,7 @@ class VaultObserverExceptionTest { CreateStateFlow.ErrorTarget.ServiceSqlSyntaxError, CreateStateFlow.ErrorTarget.ServiceSwallowErrors)) val flowResult = flowHandle.returnValue - flowResult.getOrThrow(30.seconds) + flowResult.getOrThrow(waitForFlowDuration) } } } \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 33953c76df..a5085085d4 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -26,7 +26,6 @@ import net.corda.testing.node.internal.genericDriver import net.corda.testing.node.internal.getTimestampAsDirectoryName import net.corda.testing.node.internal.newContext import rx.Observable -import java.io.File import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.atomic.AtomicInteger @@ -67,8 +66,6 @@ interface NodeHandle : AutoCloseable { fun stop() } -fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() - /** Interface which represents an out of process node and exposes its process handle. **/ @DoNotImplement interface OutOfProcess : NodeHandle { diff --git a/testing/test-common/src/main/resources/log4j2-test.xml b/testing/test-common/src/main/resources/log4j2-test.xml index 45910c8ca5..d8d489af1f 100644 --- a/testing/test-common/src/main/resources/log4j2-test.xml +++ b/testing/test-common/src/main/resources/log4j2-test.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} @@ -63,14 +63,17 @@ + + + diff --git a/testing/test-db/src/test/resources/log4j2-test.xml b/testing/test-db/src/test/resources/log4j2-test.xml index 35b51709ed..d0941363a9 100644 --- a/testing/test-db/src/test/resources/log4j2-test.xml +++ b/testing/test-db/src/test/resources/log4j2-test.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} @@ -65,14 +65,17 @@ + + + From 1d43471709f72b5798ae57c7f490fd4784f0a641 Mon Sep 17 00:00:00 2001 From: Waldemar Zurowski Date: Fri, 18 Sep 2020 08:09:20 +0200 Subject: [PATCH 11/16] Refactoring of regression Jenkins configuration * removed archiving of Gradle JUnit HTML reports * collection of JUnit tests moved to stage in parallel branch --- .ci/dev/regression/Jenkinsfile | 47 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 7efc7a3b72..f773438b4b 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -126,7 +126,6 @@ pipeline { post { always { archiveArtifacts artifacts: '**/*.log', fingerprint: false - archiveArtifacts artifacts: '**/build/reports/tests/**', fingerprint: false junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true /* * Copy all JUnit results files into a single top level directory. @@ -196,6 +195,29 @@ pipeline { } } stage('Same agent') { + post { + always { + archiveArtifacts artifacts: '**/*.log', fingerprint: false + junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true + /* + * Copy all JUnit results files into a single top level directory. + * This is necessary to stop the allure plugin from hitting out + * of memory errors due to being passed many directories with + * long paths. + * + * File names are pre-pended with a prefix when + * copied to avoid collisions between files where the same test + * classes have run on multiple agents. + */ + fileOperations([fileCopyOperation( + includes: '**/build/test-results/**/*.xml', + targetLocation: 'allure-input', + flattenFiles: true, + renameFiles: true, + sourceCaptureExpression: '.*/([^/]+)$', + targetNameExpression: 'same-agent-$1')]) + } + } stages { stage('Integration Test') { steps { @@ -273,30 +295,9 @@ pipeline { post { always { - archiveArtifacts artifacts: '**/*.log', fingerprint: false - archiveArtifacts artifacts: '**/build/reports/tests/**', fingerprint: false - junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true - unstash 'allure-input' - script { try { - /* - * Copy all JUnit results files into a single top level directory. - * This is necessary to stop the allure plugin from hitting out - * of memory errors due to being passed many directories with - * long paths. - * - * File names are pre-pended with a prefix when - * copied to avoid collisions between files where the same test - * classes have run on multiple agents. - */ - fileOperations([fileCopyOperation( - includes: '**/build/test-results/**/*.xml', - targetLocation: 'allure-input', - flattenFiles: true, - renameFiles: true, - sourceCaptureExpression: '.*/([^/]+)$', - targetNameExpression: 'same-agent-$1')]) + unstash 'allure-input' allure includeProperties: false, jdk: '', results: [[path: '**/allure-input']] From c8216be92c87a5f544d6ddc126368eb7f25f172c Mon Sep 17 00:00:00 2001 From: Waldemar Zurowski Date: Fri, 18 Sep 2020 08:14:26 +0200 Subject: [PATCH 12/16] Refactoring of regression Jenkins configuration * removed archiving of Gradle JUnit HTML reports * collection of JUnit tests moved to stage in parallel branch --- .ci/dev/regression/Jenkinsfile | 207 ++++++++++++++++++++++----------- 1 file changed, 138 insertions(+), 69 deletions(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index b805ff0fd8..f773438b4b 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -52,10 +52,15 @@ String COMMON_GRADLE_PARAMS = [ pipeline { agent { label 'standard' } + + /* + * List options in alphabetical order + */ options { - timestamps() - timeout(time: 6, unit: 'HOURS') buildDiscarder(logRotator(daysToKeepStr: '14', artifactDaysToKeepStr: '14')) + parallelsAlwaysFailFast() + timeout(time: 6, unit: 'HOURS') + timestamps() } parameters { @@ -85,6 +90,11 @@ pipeline { } } + stage('Stash') { + steps { + stash name: 'compiled', useDefaultExcludes: false + } + } stage('Sonatype Check') { steps { script { @@ -104,53 +114,132 @@ pipeline { } } - stage('Unit Test') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'test' - ].join(' ') - } - } + stage('All Tests') { + parallel { + stage('Another agent') { + agent { + label 'standard' + } + options { + skipDefaultCheckout true + } + post { + always { + archiveArtifacts artifacts: '**/*.log', fingerprint: false + junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true + /* + * Copy all JUnit results files into a single top level directory. + * This is necessary to stop the allure plugin from hitting out + * of memory errors due to being passed many directories with + * long paths. + * + * File names are pre-pended with a prefix when + * copied to avoid collisions between files where the same test + * classes have run on multiple agents. + */ + fileOperations([fileCopyOperation( + includes: '**/build/test-results/**/*.xml', + targetLocation: 'allure-input', + flattenFiles: true, + renameFiles: true, + sourceCaptureExpression: '.*/([^/]+)$', + targetNameExpression: 'other-agent-$1')]) + stash name: 'allure-input', includes: 'allure-input/**', useDefaultExcludes: false + } + cleanup { + deleteDir() /* clean up our workspace */ + } + } + stages { + stage('Unstash') { + steps { + unstash 'compiled' + } + } + stage('Recompile') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'jar' + ].join(' ') + } + } + stage('Unit Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'test' + ].join(' ') + } + } + stage('Smoke Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'smokeTest' + ].join(' ') + } + } + stage('Slow Integration Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'slowIntegrationTest' + ].join(' ') + } + } + } + } + stage('Same agent') { + post { + always { + archiveArtifacts artifacts: '**/*.log', fingerprint: false + junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true + /* + * Copy all JUnit results files into a single top level directory. + * This is necessary to stop the allure plugin from hitting out + * of memory errors due to being passed many directories with + * long paths. + * + * File names are pre-pended with a prefix when + * copied to avoid collisions between files where the same test + * classes have run on multiple agents. + */ + fileOperations([fileCopyOperation( + includes: '**/build/test-results/**/*.xml', + targetLocation: 'allure-input', + flattenFiles: true, + renameFiles: true, + sourceCaptureExpression: '.*/([^/]+)$', + targetNameExpression: 'same-agent-$1')]) + } + } + stages { + stage('Integration Test') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'integrationTest' + ].join(' ') + } + } - stage('Integration Test') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'integrationTest' - ].join(' ') - } - } - - stage('Smoke Test') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'smokeTest' - ].join(' ') - } - } - - stage('Slow Integration Test') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'slowIntegrationTest' - ].join(' ') - } - } - - stage('Deploy Node') { - steps { - sh script: [ - './gradlew', - COMMON_GRADLE_PARAMS, - 'deployNode' - ].join(' ') + stage('Deploy Node') { + steps { + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'deployNode' + ].join(' ') + } + } + } + } } } @@ -206,29 +295,9 @@ pipeline { post { always { - archiveArtifacts artifacts: '**/*.log', fingerprint: false - archiveArtifacts artifacts: '**/build/reports/tests/**', fingerprint: false - junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true - script { try { - /* - * Copy all JUnit results files into a single top level directory. - * This is necessary to stop the allure plugin from hitting out - * of memory errors due to being passed many directories with - * long paths. - * - * File names are pre-pended with the pod number when - * copied to avoid collisions between files where the same test - * classes have run on multiple pods. - */ - fileOperations([fileCopyOperation( - includes: '**/test-results-xml/**/test-runs/test-reports/**', - targetLocation: 'allure-input', - flattenFiles: true, - renameFiles: true, - sourceCaptureExpression: '.*test-results-xml/.*-([\\d]+)/.*/([^/]+)$', - targetNameExpression: '$1-$2')]) + unstash 'allure-input' allure includeProperties: false, jdk: '', results: [[path: '**/allure-input']] From 25ab7667a5959cae5390f6db0b5c22b1ee535444 Mon Sep 17 00:00:00 2001 From: Jerome Gerakis <66950409+J-Gerakis@users.noreply.github.com> Date: Thu, 24 Sep 2020 14:30:35 +0100 Subject: [PATCH 13/16] backport of bugfix ENT-5752 into OS 4.3 (#6724) (#6744) --- .../net/corda/node/services/network/NetworkMapUpdater.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt index 7ff18232a2..aba03a5074 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapUpdater.kt @@ -153,6 +153,11 @@ class NetworkMapUpdater(private val networkMapCache: NetworkMapCacheInternal, val nextScheduleDelay = try { updateNetworkMapCache() } catch (e: Exception) { + // Check to see if networkmap was reachable before and cached information exists + if (networkMapCache.allNodeHashes.size > 1) { + logger.debug("Networkmap Service unreachable but more than one nodeInfo entries found in the cache. Allowing node start-up to proceed.") + networkMapCache.nodeReady.set(null) + } logger.warn("Error encountered while updating network map, will retry in $defaultRetryInterval", e) defaultRetryInterval } From 8048cb496636d20ed12822d5fb458af607e669a4 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Fri, 25 Sep 2020 09:54:59 +0100 Subject: [PATCH 14/16] CORDA-3845: Update BC, log4j, slf4j (#6699) (#6742) * CORDA-3845: Update BC, log4j, slf4j (#6699) * CORDA-3845: Update BC to 1.64 * CORDA-3845: Upgraded log4j to 2.12.1 * We can remove the use of Manifests from the logging package so that when _it_ logs it doesn't error on the fact the stream was already closed by the default Java logger. * Remove the logging package as a plugin * latest BC version * Remove old test * Fix some rebased changes to log file handling * Update slf4j too Co-authored-by: Ryan Fowler Co-authored-by: Adel El-Beik --- build.gradle | 4 +- .../common/logging/ErrorCodeRewritePolicy.kt | 28 -------- config/dev/log4j2.xml | 6 +- constants.properties | 4 +- .../net/corda/node/NodeConfigParsingTests.kt | 8 ++- .../node/logging/ErrorCodeLoggingTests.kt | 68 ------------------- .../kotlin/net/corda/testing/driver/Driver.kt | 3 + .../src/main/resources/log4j2-test.xml | 5 +- .../src/test/resources/log4j2-test.xml | 5 +- 9 files changed, 16 insertions(+), 115 deletions(-) delete mode 100644 common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt delete mode 100644 node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt diff --git a/build.gradle b/build.gradle index 4fa1b5fb68..174018ac95 100644 --- a/build.gradle +++ b/build.gradle @@ -68,8 +68,8 @@ buildscript { ext.jersey_version = '2.25' ext.servlet_version = '4.0.1' ext.assertj_version = '3.12.2' - ext.slf4j_version = '1.7.26' - ext.log4j_version = '2.11.2' + ext.slf4j_version = '1.7.30' + ext.log4j_version = '2.12.1' ext.bouncycastle_version = constants.getProperty("bouncycastleVersion") ext.guava_version = constants.getProperty("guavaVersion") ext.caffeine_version = constants.getProperty("caffeineVersion") diff --git a/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt b/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt deleted file mode 100644 index 5f0e24bef9..0000000000 --- a/common/logging/src/main/kotlin/net/corda/common/logging/ErrorCodeRewritePolicy.kt +++ /dev/null @@ -1,28 +0,0 @@ -package net.corda.common.logging - -import org.apache.logging.log4j.core.Core -import org.apache.logging.log4j.core.LogEvent -import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy -import org.apache.logging.log4j.core.config.plugins.Plugin -import org.apache.logging.log4j.core.config.plugins.PluginFactory -import org.apache.logging.log4j.core.impl.Log4jLogEvent - -@Plugin(name = "ErrorCodeRewritePolicy", category = Core.CATEGORY_NAME, elementType = "rewritePolicy", printObject = false) -class ErrorCodeRewritePolicy : RewritePolicy { - override fun rewrite(source: LogEvent): LogEvent? { - val newMessage = source.message?.withErrorCodeFor(source.thrown, source.level) - return if (newMessage == source.message) { - source - } else { - Log4jLogEvent.Builder(source).setMessage(newMessage).build() - } - } - - companion object { - @JvmStatic - @PluginFactory - fun createPolicy(): ErrorCodeRewritePolicy { - return ErrorCodeRewritePolicy() - } - } -} \ No newline at end of file diff --git a/config/dev/log4j2.xml b/config/dev/log4j2.xml index fe67301f84..6593f84ae8 100644 --- a/config/dev/log4j2.xml +++ b/config/dev/log4j2.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} @@ -172,21 +172,17 @@ - - - - diff --git a/constants.properties b/constants.properties index 1ca16d18ee..2ed3714aec 100644 --- a/constants.properties +++ b/constants.properties @@ -20,8 +20,8 @@ quasarClassifier=jdk8 quasarVersion11=0.8.0_r3 jdkClassifier11=jdk11 proguardVersion=6.1.1 -bouncycastleVersion=1.61 -classgraphVersion=4.8.58 +bouncycastleVersion=1.66 +classgraphVersion=4.8.78 disruptorVersion=3.4.2 typesafeConfigVersion=1.3.4 jsr305Version=3.0.2 diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt index fd2f7d7507..5f243fe780 100644 --- a/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/NodeConfigParsingTests.kt @@ -1,17 +1,21 @@ package net.corda.node +import net.corda.core.internal.div import net.corda.core.utilities.getOrThrow -import net.corda.node.logging.logFile import net.corda.testing.driver.DriverParameters +import net.corda.testing.driver.NodeHandle import net.corda.testing.driver.NodeParameters import net.corda.testing.driver.driver import net.corda.testing.driver.internal.incrementalPortAllocation import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.Test import org.junit.Assert.assertTrue - +import java.io.File class NodeConfigParsingTests { + companion object { + fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() + } @Test(timeout=300_000) fun `config is overriden by underscore variable`() { diff --git a/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt b/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt deleted file mode 100644 index e1db95e528..0000000000 --- a/node/src/integration-test/kotlin/net/corda/node/logging/ErrorCodeLoggingTests.kt +++ /dev/null @@ -1,68 +0,0 @@ -package net.corda.node.logging - -import net.corda.core.flows.FlowLogic -import net.corda.core.flows.InitiatingFlow -import net.corda.core.flows.StartableByRPC -import net.corda.core.internal.div -import net.corda.core.messaging.FlowHandle -import net.corda.core.messaging.startFlow -import net.corda.core.utilities.getOrThrow -import net.corda.testing.driver.DriverParameters -import net.corda.testing.driver.NodeHandle -import net.corda.testing.driver.driver -import org.assertj.core.api.Assertions.assertThat -import org.junit.Test -import java.io.File - -class ErrorCodeLoggingTests { - @Test(timeout=300_000) - fun `log entries with a throwable and ERROR or WARN get an error code appended`() { - driver(DriverParameters(notarySpecs = emptyList())) { - val node = startNode(startInSameProcess = false).getOrThrow() - node.rpc.startFlow(::MyFlow).waitForCompletion() - val logFile = node.logFile() - - val linesWithErrorCode = logFile.useLines { lines -> - lines.filter { line -> - line.contains("[errorCode=") - }.filter { line -> - line.contains("moreInformationAt=https://errors.corda.net/") - }.toList() - } - - assertThat(linesWithErrorCode).isNotEmpty - } - } - - // This is used to detect broken logging which can be caused by loggers being initialized - // before the initLogging() call is made - @Test(timeout=300_000) - fun `When logging is set to error level, there are no other levels logged after node startup`() { - driver(DriverParameters(notarySpecs = emptyList())) { - val node = startNode(startInSameProcess = false, logLevelOverride = "ERROR").getOrThrow() - val logFile = node.logFile() - val lengthAfterStart = logFile.length() - node.rpc.startFlow(::MyFlow).waitForCompletion() - // An exception thrown in a flow will log at the "INFO" level. - assertThat(logFile.length()).isEqualTo(lengthAfterStart) - } - } - - @StartableByRPC - @InitiatingFlow - class MyFlow : FlowLogic() { - override fun call(): String { - throw IllegalArgumentException("Mwahahahah") - } - } -} - -private fun FlowHandle<*>.waitForCompletion() { - try { - returnValue.getOrThrow() - } catch (e: Exception) { - // This is expected to throw an exception, using getOrThrow() just to wait until done. - } -} - -fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() \ No newline at end of file diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index 8c16c40a34..6481a29714 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -26,6 +26,7 @@ import net.corda.testing.node.internal.genericDriver import net.corda.testing.node.internal.getTimestampAsDirectoryName import net.corda.testing.node.internal.newContext import rx.Observable +import java.io.File import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.atomic.AtomicInteger @@ -66,6 +67,8 @@ interface NodeHandle : AutoCloseable { fun stop() } +fun NodeHandle.logFile(): File = (baseDirectory / "logs").toFile().walk().filter { it.name.startsWith("node-") && it.extension == "log" }.single() + /** Interface which represents an out of process node and exposes its process handle. **/ @DoNotImplement interface OutOfProcess : NodeHandle { diff --git a/testing/test-common/src/main/resources/log4j2-test.xml b/testing/test-common/src/main/resources/log4j2-test.xml index d8d489af1f..45910c8ca5 100644 --- a/testing/test-common/src/main/resources/log4j2-test.xml +++ b/testing/test-common/src/main/resources/log4j2-test.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} @@ -63,17 +63,14 @@ - - - diff --git a/testing/test-db/src/test/resources/log4j2-test.xml b/testing/test-db/src/test/resources/log4j2-test.xml index d0941363a9..35b51709ed 100644 --- a/testing/test-db/src/test/resources/log4j2-test.xml +++ b/testing/test-db/src/test/resources/log4j2-test.xml @@ -1,5 +1,5 @@ - + ${sys:log-path:-logs} @@ -65,17 +65,14 @@ - - - From 5c1ca20daeac0556cbad9d4f75239bd7bec2b54b Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Fri, 25 Sep 2020 10:38:46 +0100 Subject: [PATCH 15/16] Detekt --- node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt index 7314df162c..487920ff88 100644 --- a/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt +++ b/node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt @@ -25,7 +25,6 @@ import org.junit.runners.Parameterized import java.sql.Connection import java.sql.Statement import java.util.* -import javax.sql.DataSource import kotlin.test.assertFailsWith /* From 396671cb87e1e464240c4067bc159075d34d0e89 Mon Sep 17 00:00:00 2001 From: Denis Rekalov Date: Wed, 30 Sep 2020 13:39:26 +0100 Subject: [PATCH 16/16] CORDA-4043: Generate 16-octets certificate serial numbers (#6746) --- .../internal/crypto/X509UtilitiesTest.kt | 14 ++++++++++++++ .../nodeapi/internal/crypto/X509Utilities.kt | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt index 36def7555e..dffe0e218a 100644 --- a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt +++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt @@ -44,6 +44,7 @@ import net.corda.coretesting.internal.NettyTestServer import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.coretesting.internal.stubs.CertificateStoreStubs import net.corda.nodeapi.internal.crypto.CertificateType +import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.checkValidity import net.corda.nodeapi.internal.crypto.getSupportedKey @@ -51,6 +52,7 @@ import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore import net.corda.nodeapi.internal.crypto.save import net.corda.nodeapi.internal.crypto.toBc import net.corda.nodeapi.internal.crypto.x509 +import net.corda.nodeapi.internal.crypto.x509Certificates import net.i2p.crypto.eddsa.EdDSAPrivateKey import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.asn1.x509.* @@ -565,4 +567,16 @@ class X509UtilitiesTest { cert.checkValidity({ "Error text" }, { }, Date.from(today.toInstant() + 51.days)) } } + + @Test(timeout = 300_000) + fun `check certificate serial number`() { + val keyPair = generateKeyPair() + val subject = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") + val cert = X509Utilities.createSelfSignedCACertificate(subject, keyPair) + assertTrue(cert.serialNumber.signum() > 0) + assertEquals(127, cert.serialNumber.bitLength()) + val serialized = X509Utilities.buildCertPath(cert).encoded + val deserialized = X509CertificateFactory().delegate.generateCertPath(serialized.inputStream()).x509Certificates.first() + assertEquals(cert.serialNumber, deserialized.serialNumber) + } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt index 138cb23d37..841d105891 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt @@ -2,7 +2,7 @@ package net.corda.nodeapi.internal.crypto import net.corda.core.CordaOID import net.corda.core.crypto.Crypto -import net.corda.core.crypto.random63BitValue +import net.corda.core.crypto.newSecureRandom import net.corda.core.internal.* import net.corda.core.utilities.days import net.corda.core.utilities.millis @@ -35,6 +35,8 @@ import java.time.Instant import java.time.temporal.ChronoUnit import java.util.* import javax.security.auth.x500.X500Principal +import kotlin.experimental.and +import kotlin.experimental.or object X509Utilities { // Note that this default value only applies to BCCryptoService. Other implementations of CryptoService may have to use different @@ -58,6 +60,7 @@ object X509Utilities { const val TLS_CERTIFICATE_DAYS_TO_EXPIRY_WARNING_THRESHOLD = 30 private const val KEY_ALIAS_REGEX = "[a-z0-9-]+" private const val KEY_ALIAS_MAX_LENGTH = 100 + private const val CERTIFICATE_SERIAL_NUMBER_LENGTH = 16 /** * Checks if the provided key alias does not exceed maximum length and @@ -184,7 +187,7 @@ object X509Utilities { nameConstraints: NameConstraints? = null, crlDistPoint: String? = null, crlIssuer: X500Name? = null): X509v3CertificateBuilder { - val serial = BigInteger.valueOf(random63BitValue()) + val serial = generateCertificateSerialNumber() val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded)) val role = certificateType.role @@ -364,6 +367,15 @@ object X509Utilities { builder.addExtension(Extension.cRLDistributionPoints, false, CRLDistPoint(arrayOf(distPoint))) } } + + @Suppress("MagicNumber") + private fun generateCertificateSerialNumber(): BigInteger { + val bytes = ByteArray(CERTIFICATE_SERIAL_NUMBER_LENGTH) + newSecureRandom().nextBytes(bytes) + // Set highest byte to 01xxxxxx to ensure positive sign and constant bit length. + bytes[0] = bytes[0].and(0x3F).or(0x40) + return BigInteger(bytes) + } } // Assuming cert type to role is 1:1