From bcce0e95195ea9dee1b66aaaae58d9519617b6a4 Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Tue, 15 Sep 2020 13:18:46 +0100 Subject: [PATCH 1/3] INFRA-683 Move OS 4.4 release branch builds to linear (#6705) * Do not use Security.addProvider(BouncyCastleProvider()) in tests, to avoid disruptions of other tests. * 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. * INFRA-683 Move Corda OS release branch builds to serial (#6703) Co-authored-by: Waldemar Zurowski Co-authored-by: Denis Rekalov Co-authored-by: Waldemar Zurowski --- .ci/dev/regression/Jenkinsfile | 159 ++++++++++-------- build.gradle | 2 +- .../crypto/X509NameConstraintsTest.kt | 4 +- .../indentity/PartyAndCertificateTest.kt | 9 + .../CertificateRevocationListNodeTests.kt | 4 +- .../services/network/NetworkMapUpdaterTest.kt | 19 ++- .../network/NetworkParametersReaderTest.kt | 9 +- .../services/network/NodeInfoWatcherTest.kt | 5 + .../persistence/NodeAttachmentServiceTest.kt | 7 +- .../NetworkRegistrationHelperTest.kt | 8 +- 10 files changed, 145 insertions(+), 81 deletions(-) diff --git a/.ci/dev/regression/Jenkinsfile b/.ci/dev/regression/Jenkinsfile index 0e31ff7305..388d64360c 100644 --- a/.ci/dev/regression/Jenkinsfile +++ b/.ci/dev/regression/Jenkinsfile @@ -1,29 +1,21 @@ #!groovy /** - * Jenkins pipeline to build Corda OS release branches and tags + * Jenkins pipeline to build Corda OS release branches and tags. + * PLEASE NOTE: we DO want to run a build for each commit!!! */ -/** - * Kill already started job. - * Assume new commit takes precendence and results from previous - * unfinished builds are not required. - * This feature doesn't play well with disableConcurrentBuilds() option - */ -@Library('corda-shared-build-pipeline-steps') -import static com.r3.build.BuildControl.killAllExistingBuildsForJob - -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 */ @@ -89,54 +104,53 @@ pipeline { } } - stage('Deploy Nodes') { + stage('Unit Test') { steps { - sh "./gradlew --no-daemon jar deployNodes" + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'test' + ].join(' ') } } - stage('Generate Build Image') { + stage('Integration Test') { steps { - withCredentials([string(credentialsId: 'container_reg_passwd', variable: 'DOCKER_PUSH_PWD')]) { - sh "./gradlew " + - "-Dkubenetize=true " + - "-Ddocker.push.password=\"\${DOCKER_PUSH_PWD}\" " + - "-Ddocker.work.dir=\"/tmp/\${EXECUTOR_NUMBER}\" " + - "-Ddocker.build.tag=\"\${DOCKER_TAG_TO_USE}\"" + - " clean preAllocateForParallelRegressionTest preAllocateForAllParallelSlowIntegrationTest pushBuildImage --stacktrace" - } - sh "kubectl auth can-i get pods" + sh script: [ + './gradlew', + COMMON_GRADLE_PARAMS, + 'integrationTest' + ].join(' ') } } - stage('Testing phase') { - parallel { - stage('Regression 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" - } - } - stage('Slow Integration 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}\" " + - " allParallelSlowIntegrationTest --stacktrace" - } - } + 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(' ') } } @@ -172,24 +186,29 @@ 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(' ') } } } } - post { always { - archiveArtifacts artifacts: '**/pod-logs/**/*.log', fingerprint: false - junit testResults: '**/build/test-results-xml/**/*.xml', keepLongStdio: true + archiveArtifacts artifacts: '**/*.log', fingerprint: false + archiveArtifacts artifacts: '**/build/reports/tests/**', fingerprint: false + junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true script { try { @@ -211,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") @@ -240,6 +259,8 @@ pipeline { statusSymbol = '\u2705' break; case 'UNSTABLE': + statusSymbol = '\u26A0' + break; case 'FAILURE': statusSymbol = '\u274c' break; diff --git a/build.gradle b/build.gradle index 241c85b5a6..4fa1b5fb68 100644 --- a/build.gradle +++ b/build.gradle @@ -315,7 +315,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/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/crypto/X509NameConstraintsTest.kt index 98f5e74a18..1b7170173d 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(timeout=300_000) 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/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt b/core-tests/src/test/kotlin/net/corda/coretests/indentity/PartyAndCertificateTest.kt index 436e222b86..edc91eac91 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(timeout=300_000) fun `reject a path with no roles`() { val path = X509Utilities.buildCertPath(DEV_ROOT_CA.certificate) 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 9512674d01..d420aba7e2 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() 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 48ac12a1f9..0e239f8cf5 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 @@ -63,6 +63,8 @@ import org.junit.Test 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 @@ -80,11 +82,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 @@ -96,6 +99,14 @@ 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() + + 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/network/NetworkParametersReaderTest.kt b/node/src/test/kotlin/net/corda/node/services/network/NetworkParametersReaderTest.kt index 6a856d3246..1886c35ea7 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 99f08b5a17..3412511e6c 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 50bd56921d..b350ce6107 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 @@ -24,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 @@ -68,6 +68,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() @@ -91,6 +95,7 @@ class NodeAttachmentServiceTest { @After fun tearDown() { database.close() + fs.close() } @Test(timeout=300_000) 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 caeacc5b71..c8c6c3204b 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 @@ -38,6 +38,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import java.nio.file.Files +import java.nio.file.FileSystem import java.security.PublicKey import java.security.cert.CertPathValidatorException import java.security.cert.X509Certificate @@ -47,7 +48,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 @@ -56,6 +57,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 ebd36c22e7c77f338680d827cde37bdd60a3c23a Mon Sep 17 00:00:00 2001 From: Waldemar Zurowski Date: Tue, 15 Sep 2020 15:42:17 +0200 Subject: [PATCH 2/3] Removed unused import --- .../corda/node/services/network/NetworkParametersReaderTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 a15ca7717e..523710e74c 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 @@ -21,7 +21,6 @@ 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 @@ -91,4 +90,4 @@ class NetworkParametersReaderTest { val parameters = inByteArray.deserialize() assertThat(parameters.verified().eventHorizon).isEqualTo(Int.MAX_VALUE.days) } -} \ No newline at end of file +} From c387c2cb951b452083fc9fd28431754722738d9e Mon Sep 17 00:00:00 2001 From: Waldemar Zurowski Date: Wed, 16 Sep 2020 10:23:11 +0200 Subject: [PATCH 3/3] Synchronised changes with OS 4.4 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()