From 12546c0a7c2e20b822678726b927d3a6754816b9 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Wed, 10 Jan 2018 11:32:24 +0000 Subject: [PATCH] [ENT-1281]: Disable database.runMigration by default and enforce database version on startup. (#264) * [ENT-1281]: set database.runMigration=false by default and add state check at startup * [ENT-1281]: attempt to fix tests * [ENT-1281]: attempt to fix tests * [ENT-1281]: set runMigration=true in the cordformation plugin * [ENT-1281]: attempt to fix tests * [ENT-1281]: attempt to fix tests * [ENT-1281]: attempt to fix tests * [ENT-1281]: fix formatting * [ENT-1281]: typo and javadocs * [ENT-1281]: small refactoring and added test for SchemaMigration * [ENT-1281]: update documentation to reflect changes * [ENT-1281]: fix tests after merge * [ENT-1339]: for h2, allow schemas without migrations to run (#294) * [ENT-1339]: for h2, allow schemas without migrations to run * [ENT-1339]: fix various migration issues and change author name * [ENT-1339]: add naming convention for migrations * [ENT-1339]: change naming convention to use hyphens * [ENT-1339]: change mapping of participants to be able to control the table name * [ENT-1339]: change FK names to <=30 for oracle 11g compatibility * [ENT-1339]: cmd line argument for migrations made consistent * [ENT-1339]: revert abstract state superclasses * Update db integration test setup - new tables. * Update db integration test setup - new tables. * [ENT-1339]: remove final from participants to allow table name config * [ENT-1339]: shortened pk * [ENT-1339]: revert constructor * [ENT-1339]: change getMigrationResource api to Nullable * fix compile error * [ENT-1281]: fix tests after merge * [ENT-1281]: fix tests after merge --- .../rpc/StandaloneCordaRPCJavaClientTest.java | 3 +- docs/source/api-persistence.rst | 3 +- .../src/main/kotlin/net/corda/plugins/Node.kt | 1 + .../doorman/DoormanIntegrationTest.kt | 0 .../doorman/NodeRegistrationTest.kt | 3 +- .../hsm/SigningServiceIntegrationTest.kt | 6 +- .../common/persistence/PersistenceUtils.kt | 4 +- ...PersistentCertificateRequestStorageTest.kt | 3 +- .../PersistentNetworkMapStorageTest.kt | 3 +- .../PersistentNodeInfoStorageTest.kt | 3 +- .../internal/persistence/CordaPersistence.kt | 2 +- .../internal/persistence/SchemaMigration.kt | 41 ++++++++++--- .../net/corda/node/internal/AbstractNode.kt | 6 +- .../net/corda/node/InteractiveShellTest.kt | 2 +- .../PersistentIdentityServiceTests.kt | 2 +- .../messaging/ArtemisMessagingTests.kt | 2 +- .../persistence/DBCheckpointStorageTests.kt | 2 +- .../persistence/DBTransactionStorageTests.kt | 2 +- .../persistence/HibernateConfigurationTest.kt | 2 +- .../persistence/NodeAttachmentStorageTest.kt | 2 +- .../persistence/SchemaMigrationTest.kt | 59 +++++++++++++++++++ .../services/schema/HibernateObserverTests.kt | 2 +- .../DistributedImmutableMapTests.kt | 2 +- .../PersistentUniquenessProviderTests.kt | 2 +- .../node/services/vault/VaultQueryTests.kt | 2 +- .../corda/node/utilities/ObservablesTests.kt | 2 +- .../corda/irs/api/NodeInterestRatesTest.kt | 2 +- .../net/corda/testing/node/MockServices.kt | 2 +- .../testing/node/internal/DriverDSLImpl.kt | 5 +- .../testing/node/internal/NodeBasedTest.kt | 1 + .../net/corda/smoketesting/NodeConfig.kt | 4 +- 31 files changed, 135 insertions(+), 40 deletions(-) create mode 100644 network-management/src/integration-test/kotlin/com/r3/corda/networkmanage/doorman/DoormanIntegrationTest.kt create mode 100644 node/src/test/kotlin/net/corda/node/services/persistence/SchemaMigrationTest.kt diff --git a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java index 30edeeb897..32e2747072 100644 --- a/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java +++ b/client/rpc/src/smoke-test/java/net/corda/java/rpc/StandaloneCordaRPCJavaClientTest.java @@ -48,7 +48,8 @@ public class StandaloneCordaRPCJavaClientTest { port.getAndIncrement(), port.getAndIncrement(), true, - Collections.singletonList(rpcUser) + Collections.singletonList(rpcUser), + true ); @Before diff --git a/docs/source/api-persistence.rst b/docs/source/api-persistence.rst index ba11b26cd7..ee3b0352c7 100644 --- a/docs/source/api-persistence.rst +++ b/docs/source/api-persistence.rst @@ -151,6 +151,7 @@ As a database migration tool, we use the open source library liquibase server.start(NetworkHostAndPort(HOST, 0), database, networkMapServiceParameter = null, doormanServiceParameter = DoormanConfig(approveAll = true, approveInterval = 2.seconds.toMillis(), jiraConfig = null), updateNetworkParameters = null) @@ -99,7 +99,7 @@ class SigningServiceIntegrationTest { doReturn(ALICE_NAME).whenever(it).myLegalName doReturn(URL("http://${doormanHostAndPort.host}:${doormanHostAndPort.port}")).whenever(it).compatibilityZoneURL } - val signingServiceStorage = DBSignedCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties())) + val signingServiceStorage = DBSignedCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true))) val hsmSigner = givenSignerSigningAllRequests(signingServiceStorage) // Poll the database for approved requests @@ -143,7 +143,7 @@ class SigningServiceIntegrationTest { @Test fun `DEMO - Create CSR and poll`() { //Start doorman server - val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig()) + val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) NetworkManagementServer().use { server -> server.start(NetworkHostAndPort(HOST, 0), database, networkMapServiceParameter = null, doormanServiceParameter = DoormanConfig(approveAll = true, approveInterval = 2.seconds.toMillis(), jiraConfig = null), updateNetworkParameters = null) diff --git a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistenceUtils.kt b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistenceUtils.kt index 9d4a45dbf8..194e363725 100644 --- a/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistenceUtils.kt +++ b/network-management/src/main/kotlin/com/r3/corda/networkmanage/common/persistence/PersistenceUtils.kt @@ -38,9 +38,7 @@ fun configureDatabase(dataSourceProperties: Properties, val dataSource = HikariDataSource(config) val schemas = setOf(NetworkManagementSchemaServices.SchemaV1) - if (databaseConfig.runMigration) { - SchemaMigration(schemas, dataSource, true, databaseConfig.schema).runMigration() - } + SchemaMigration(schemas, dataSource, true, databaseConfig).nodeStartup() return CordaPersistence(dataSource, databaseConfig, schemas, config.dataSourceProperties.getProperty("url", ""), emptyList()) } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt index 7825622855..ec16998e8f 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentCertificateRequestStorageTest.kt @@ -10,6 +10,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.testing.internal.createDevNodeCaCertPath +import net.corda.nodeapi.internal.persistence.DatabaseConfig import org.assertj.core.api.Assertions.assertThat import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest @@ -28,7 +29,7 @@ class PersistentCertificateRequestStorageTest : TestBase() { @Before fun startDb() { - persistence = configureDatabase(makeTestDataSourceProperties()) + persistence = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) storage = PersistentCertificateRequestStorage(persistence) } diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt index 70bd9dc6e1..1ca4abd931 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNetworkMapStorageTest.kt @@ -12,6 +12,7 @@ import net.corda.nodeapi.internal.crypto.X509CertificateFactory import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.SignedNetworkMap 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.internal.TestNodeInfoBuilder import net.corda.testing.internal.createDevIntermediateCaCertPath @@ -37,7 +38,7 @@ class PersistentNetworkMapStorageTest : TestBase() { val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() rootCaCert = rootCa.certificate this.intermediateCa = intermediateCa - persistence = configureDatabase(makeTestDataSourceProperties()) + persistence = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) networkMapStorage = PersistentNetworkMapStorage(persistence, LocalSigner(intermediateCa.keyPair, arrayOf(intermediateCa.certificate, rootCaCert))) nodeInfoStorage = PersistentNodeInfoStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence) diff --git a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorageTest.kt b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorageTest.kt index 7fd0f9cdf7..b20d30a188 100644 --- a/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorageTest.kt +++ b/network-management/src/test/kotlin/com/r3/corda/networkmanage/common/persistence/PersistentNodeInfoStorageTest.kt @@ -14,6 +14,7 @@ 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.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.testing.internal.TestNodeInfoBuilder import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.signWith @@ -41,7 +42,7 @@ class PersistentNodeInfoStorageTest : TestBase() { val (rootCa, intermediateCa) = createDevIntermediateCaCertPath() rootCaCert = rootCa.certificate this.intermediateCa = intermediateCa - persistence = configureDatabase(MockServices.makeTestDataSourceProperties()) + persistence = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(runMigration = true)) nodeInfoStorage = PersistentNodeInfoStorage(persistence) requestStorage = PersistentCertificateRequestStorage(persistence) } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index 5289bdf302..7575b65101 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -18,7 +18,7 @@ const val NODE_DATABASE_PREFIX = "node_" // This class forms part of the node config and so any changes to it must be handled with care data class DatabaseConfig( - val runMigration: Boolean = true, + val runMigration: Boolean = false, val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ, val schema: String? = null, val exportHibernateJMXStatistics: Boolean = false diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt index 3675c9b0bd..fad9663ddd 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/SchemaMigration.kt @@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.persistence import com.fasterxml.jackson.databind.ObjectMapper import liquibase.Contexts +import liquibase.LabelExpression import liquibase.Liquibase import liquibase.database.Database import liquibase.database.DatabaseFactory @@ -14,17 +15,34 @@ import net.corda.core.utilities.contextLogger import java.io.* import javax.sql.DataSource -class SchemaMigration(val schemas: Set, val dataSource: DataSource, val failOnMigrationMissing: Boolean, private val schemaName: String? = null) { +class SchemaMigration(val schemas: Set, val dataSource: DataSource, val failOnMigrationMissing: Boolean, private val databaseConfig: DatabaseConfig) { companion object { private val logger = contextLogger() } - fun generateMigrationScript(outputFile: File) = doRunMigration(PrintWriter(outputFile)) + /** + * Main entry point to the schema migration. + * Called during node startup. + */ + fun nodeStartup() = if (databaseConfig.runMigration) runMigration() else checkState() - fun runMigration() = doRunMigration() + /** + * will run the liquibase migration on the actual database + */ + fun runMigration() = doRunMigration(run = true, outputWriter = null, check = false) - private fun doRunMigration(outputWriter: Writer? = null) { + /** + * will write the migration to the outputFile + */ + fun generateMigrationScript(outputFile: File) = doRunMigration(run = false, outputWriter = PrintWriter(outputFile), check = false) + + /** + * ensures that the database is up to date with the latest migration changes + */ + fun checkState() = doRunMigration(run = false, outputWriter = null, check = true) + + private fun doRunMigration(run: Boolean, outputWriter: Writer?, check: Boolean) { // virtual file name of the changelog that includes all schemas val dynamicInclude = "master.changelog.json" @@ -65,6 +83,7 @@ class SchemaMigration(val schemas: Set, val dataSource: DataSource val liquibase = Liquibase(dynamicInclude, customResourceAccessor, getLiquibaseDatabase(JdbcConnection(connection))) + val schemaName: String? = databaseConfig.schema if (!schemaName.isNullOrBlank()) { if (liquibase.database.defaultSchemaName != schemaName) { logger.debug("defaultSchemaName=${liquibase.database.defaultSchemaName} changed to $schemaName") @@ -79,10 +98,16 @@ class SchemaMigration(val schemas: Set, val dataSource: DataSource logger.info("liquibaseSchemaName=${liquibase.database.liquibaseSchemaName}") logger.info("outputDefaultSchema=${liquibase.database.outputDefaultSchema}") - if (outputWriter != null) { - liquibase.update(Contexts(), outputWriter) - } else { - liquibase.update(Contexts()) + when { + run && !check -> liquibase.update(Contexts()) + check && !run -> { + val unRunChanges = liquibase.listUnrunChangeSets(Contexts(), LabelExpression()) + if (unRunChanges.isNotEmpty()) { + throw Exception("There are ${unRunChanges.size} outstanding database changes that need to be run. Please use the provided tools to update the database.") + } + } + (outputWriter != null) && !check && !run -> liquibase.update(Contexts(), outputWriter) + else -> throw IllegalStateException("Invalid usage.") } } } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 51aeee85d7..1b22b01d76 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -204,14 +204,14 @@ abstract class AbstractNode(val configuration: NodeConfiguration, fun generateDatabaseSchema(outputFile: String) { HikariDataSource(HikariConfig(configuration.dataSourceProperties)).use { dataSource -> val jdbcUrl = configuration.dataSourceProperties.getProperty("url", "") - SchemaMigration(cordappLoader.cordappSchemas, dataSource, !isH2Database(jdbcUrl), configuration.database.schema).generateMigrationScript(File(outputFile)) + SchemaMigration(cordappLoader.cordappSchemas, dataSource, !isH2Database(jdbcUrl), configuration.database).generateMigrationScript(File(outputFile)) } } fun runDbMigration() { HikariDataSource(HikariConfig(configuration.dataSourceProperties)).use { dataSource -> val jdbcUrl = configuration.dataSourceProperties.getProperty("url", "") - SchemaMigration(cordappLoader.cordappSchemas, dataSource, !isH2Database(jdbcUrl), configuration.database.schema).runMigration() + SchemaMigration(cordappLoader.cordappSchemas, dataSource, !isH2Database(jdbcUrl), configuration.database).runMigration() } } @@ -887,7 +887,7 @@ fun configureDatabase(dataSourceProperties: Properties, val jdbcUrl = config.dataSourceProperties.getProperty("url", "") if (databaseConfig.runMigration) { - SchemaMigration(schemaService.schemaOptions.keys, dataSource, !isH2Database(jdbcUrl), databaseConfig.schema).runMigration() + SchemaMigration(schemaService.schemaOptions.keys, dataSource, !isH2Database(jdbcUrl), databaseConfig).runMigration() } return CordaPersistence(dataSource, databaseConfig, schemaService.schemaOptions.keys, jdbcUrl, attributeConverters) diff --git a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt index eb3e5ba532..fc5f4a1a04 100644 --- a/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt +++ b/node/src/test/kotlin/net/corda/node/InteractiveShellTest.kt @@ -32,7 +32,7 @@ class InteractiveShellTest { @Before fun setup() { - InteractiveShell.database = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) + InteractiveShell.database = configureDatabase(MockServices.makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock()) } @After diff --git a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt index 5d6cc41ce1..a52a1cdb70 100644 --- a/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/identity/PersistentIdentityServiceTests.kt @@ -49,7 +49,7 @@ class PersistentIdentityServiceTests { @Before fun setup() { identityService = PersistentIdentityService(DEV_ROOT_CA.certificate) - database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), identityService) + database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), identityService) } @After diff --git a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt index 9a90b4a1e8..98d6f0233d 100644 --- a/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/messaging/ArtemisMessagingTests.kt @@ -81,7 +81,7 @@ class ArtemisMessagingTests { doReturn(true).whenever(it).useAMQPBridges } LogHelper.setLevel(PersistentUniquenessProvider::class) - database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) + database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock()) networkMapCache = NetworkMapCacheImpl(PersistentNetworkMapCache(database, emptyList()), rigorousMock()) } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt index 4e750992dc..79c7d23011 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBCheckpointStorageTests.kt @@ -45,7 +45,7 @@ class DBCheckpointStorageTests { @Before fun setUp() { LogHelper.setLevel(PersistentUniquenessProvider::class) - database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) + database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock()) newCheckpointStorage() } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt index 3f559113da..125ce66855 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/DBTransactionStorageTests.kt @@ -40,7 +40,7 @@ class DBTransactionStorageTests { fun setUp() { LogHelper.setLevel(PersistentUniquenessProvider::class) val dataSourceProps = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProps, DatabaseConfig(), rigorousMock()) + database = configureDatabase(dataSourceProps, DatabaseConfig(runMigration = true), rigorousMock()) newTransactionStorage() } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt index 82a1e47e9a..8241f2d373 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/HibernateConfigurationTest.kt @@ -108,7 +108,7 @@ class HibernateConfigurationTest { } } val schemaService = NodeSchemaService(extraSchemas = setOf(CashSchemaV1, SampleCashSchemaV2, SampleCashSchemaV3, DummyLinearStateSchemaV1, DummyLinearStateSchemaV2, DummyDealStateSchemaV1 )) - database = configureDatabase(dataSourceProps, DatabaseConfig(), identityService, schemaService) + database = configureDatabase(dataSourceProps, DatabaseConfig(runMigration = true), identityService, schemaService) database.transaction { hibernateConfig = database.hibernateConfig // `consumeCash` expects we can self-notarise transactions diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt index 5995f3c177..272480bfec 100644 --- a/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt +++ b/node/src/test/kotlin/net/corda/node/services/persistence/NodeAttachmentStorageTest.kt @@ -44,7 +44,7 @@ class NodeAttachmentStorageTest { LogHelper.setLevel(PersistentUniquenessProvider::class) val dataSourceProperties = makeTestDataSourceProperties() - database = configureDatabase(dataSourceProperties, DatabaseConfig(), rigorousMock()) + database = configureDatabase(dataSourceProperties, DatabaseConfig(runMigration = true), rigorousMock()) fs = Jimfs.newFileSystem(Configuration.unix()) } diff --git a/node/src/test/kotlin/net/corda/node/services/persistence/SchemaMigrationTest.kt b/node/src/test/kotlin/net/corda/node/services/persistence/SchemaMigrationTest.kt new file mode 100644 index 0000000000..e9881447fb --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/services/persistence/SchemaMigrationTest.kt @@ -0,0 +1,59 @@ +package net.corda.node.services.persistence + +import com.zaxxer.hikari.HikariConfig +import com.zaxxer.hikari.HikariDataSource +import net.corda.node.internal.configureDatabase +import net.corda.node.services.schema.NodeSchemaService +import net.corda.nodeapi.internal.persistence.CordaPersistence +import net.corda.nodeapi.internal.persistence.DatabaseConfig +import net.corda.nodeapi.internal.persistence.SchemaMigration +import net.corda.testing.internal.rigorousMock +import net.corda.testing.node.MockServices +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatExceptionOfType +import org.junit.Test +import java.math.BigInteger + +class SchemaMigrationTest { + + @Test + fun `Ensure that runMigration is disabled by default`() { + assertThat(DatabaseConfig().runMigration).isFalse() + } + + @Test + fun `Migration is run when runMigration is disabled, and database is H2`() { + val dataSourceProps = MockServices.makeTestDataSourceProperties() + val db = configureDatabase(dataSourceProps, DatabaseConfig(runMigration = false), rigorousMock()) + checkMigrationRun(db) + } + + @Test + fun `Migration is run when runMigration is enabled`() { + val dataSourceProps = MockServices.makeTestDataSourceProperties() + val db = configureDatabase(dataSourceProps, DatabaseConfig(runMigration = true), rigorousMock()) + checkMigrationRun(db) + } + + @Test + fun `Verification passes when migration is run as a separate step`() { + val schemaService = NodeSchemaService() + val dataSourceProps = MockServices.makeTestDataSourceProperties() + + //run the migration on the database + val migration = SchemaMigration(schemaService.schemaOptions.keys, HikariDataSource(HikariConfig(dataSourceProps)), true, DatabaseConfig()) + migration.runMigration() + + //start the node with "runMigration = false" and check that it started correctly + val db = configureDatabase(dataSourceProps, DatabaseConfig(runMigration = false), rigorousMock(), schemaService) + checkMigrationRun(db) + } + + private fun checkMigrationRun(db: CordaPersistence) { + //check that the hibernate_sequence was created which means the migration was run + db.transaction { + val value = this.session.createNativeQuery("SELECT NEXT VALUE FOR hibernate_sequence").uniqueResult() as BigInteger + assertThat(value).isGreaterThan(BigInteger.ZERO) + } + } +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt index dcda96ce30..0a657ad95e 100644 --- a/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/schema/HibernateObserverTests.kt @@ -69,7 +69,7 @@ class HibernateObserverTests { return parent } } - val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock(), schemaService) + val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock(), schemaService) HibernateObserver.install(rawUpdatesPublisher, database.hibernateConfig, schemaService) database.transaction { val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt index cd2e965712..1c60db0ba6 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/DistributedImmutableMapTests.kt @@ -88,7 +88,7 @@ class DistributedImmutableMapTests { private fun createReplica(myAddress: NetworkHostAndPort, clusterAddress: NetworkHostAndPort? = null): CompletableFuture { val storage = Storage.builder().withStorageLevel(StorageLevel.MEMORY).build() val address = Address(myAddress.host, myAddress.port) - val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) + val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock()) databases.add(database) val stateMachineFactory = { DistributedImmutableMap(database, RaftUniquenessProvider.Companion::createMap) } diff --git a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt index f4d03c9c39..7e25d42c9e 100644 --- a/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/transactions/PersistentUniquenessProviderTests.kt @@ -29,7 +29,7 @@ class PersistentUniquenessProviderTests { @Before fun setUp() { LogHelper.setLevel(PersistentUniquenessProvider::class) - database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) + database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock()) } @After diff --git a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt index c5976b4bc7..8b48978943 100644 --- a/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/vault/VaultQueryTests.kt @@ -131,7 +131,7 @@ open class VaultQueryTests { @Ignore @Test fun createPersistentTestDb() { - val database = configureDatabase(makePersistentDataSourceProperties(), DatabaseConfig(), identitySvc) + val database = configureDatabase(makePersistentDataSourceProperties(), DatabaseConfig(runMigration = true), identitySvc) setUpDb(database, 5000) database.close() diff --git a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt index 91210f052a..68918167f0 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/ObservablesTests.kt @@ -22,7 +22,7 @@ class ObservablesTests { private val toBeClosed = mutableListOf() private fun createDatabase(): CordaPersistence { - val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) + val database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock()) toBeClosed += database return database } diff --git a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt index 1cbbadd253..98c3ff92e0 100644 --- a/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt +++ b/samples/irs-demo/cordapp/src/test/kotlin/net/corda/irs/api/NodeInterestRatesTest.kt @@ -74,7 +74,7 @@ class NodeInterestRatesTest { @Before fun setUp() { - database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(), rigorousMock()) + database = configureDatabase(makeTestDataSourceProperties(), DatabaseConfig(runMigration = true), rigorousMock()) database.transaction { oracle = createMockCordaService(services, NodeInterestRates::Oracle) oracle.knownFixes = TEST_DATA diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index 651aadf123..8a6cf3abe0 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -107,7 +107,7 @@ open class MockServices private constructor( val transactionIsolationLevel = if (config.hasPath(TRANSACTION_ISOLATION_LEVEL)) TransactionIsolationLevel.valueOf(config.getString(TRANSACTION_ISOLATION_LEVEL)) else TransactionIsolationLevel.READ_COMMITTED val schema = if (config.hasPath(SCHEMA)) config.getString(SCHEMA) else "" - return DatabaseConfig(transactionIsolationLevel = transactionIsolationLevel, schema = schema) + return DatabaseConfig(runMigration = true, transactionIsolationLevel = transactionIsolationLevel, schema = schema) } /** diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt index ca05b8a7b9..f29fddf507 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt @@ -209,6 +209,7 @@ class DriverDSLImpl( baseDirectory = baseDirectory(name), allowMissingConfig = true, configOverrides = configOf( + "database" to mapOf("runMigration" to "true"), "myLegalName" to name.toString(), "p2pAddress" to p2pAddress.toString(), "rpcAddress" to rpcAddress.toString(), @@ -227,6 +228,7 @@ class DriverDSLImpl( baseDirectory = baseDirectory, allowMissingConfig = true, configOverrides = configOf( + "database" to mapOf("runMigration" to "true"), "p2pAddress" to "localhost:1222", // required argument, not really used "compatibilityZoneURL" to compatibilityZoneURL.toString(), "myLegalName" to providedName.toString()) @@ -313,7 +315,8 @@ class DriverDSLImpl( baseDirectory = baseDirectory(name), allowMissingConfig = true, configOverrides = cordform.config + rpcAddress + notary + mapOf( - "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers + "rpcUsers" to if (rpcUsers.isEmpty()) defaultRpcUserList else rpcUsers, + "database" to mapOf("runMigration" to "true") ) )) return startNodeInternal(config, webAddress, null, "200m", localNetworkMap) diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt index c7117e3f96..93f6f32daa 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/NodeBasedTest.kt @@ -91,6 +91,7 @@ abstract class NodeBasedTest(private val cordappPackages: List = emptyLi baseDirectory = baseDirectory, allowMissingConfig = true, configOverrides = configOf( + "database" to mapOf("runMigration" to "true"), "myLegalName" to legalName.toString(), "p2pAddress" to p2pAddress, "rpcAddress" to localPort[1].toString(), diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt index 4f002c6f9d..649b2ed63f 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt @@ -14,7 +14,8 @@ class NodeConfig( val rpcPort: Int, val webPort: Int, val isNotary: Boolean, - val users: List + val users: List, + val runMigration: Boolean = true ) { companion object { val renderOptions: ConfigRenderOptions = ConfigRenderOptions.defaults().setOriginComments(false) @@ -34,6 +35,7 @@ class NodeConfig( .withValue("webAddress", addressValueFor(webPort)) .withValue("rpcAddress", addressValueFor(rpcPort)) .withValue("rpcUsers", valueFor(users.map(User::toMap).toList())) + .withValue("database", valueFor(mapOf("runMigration" to runMigration))) .withValue("useTestClock", valueFor(true)) return if (isNotary) { config.withValue("notary", ConfigValueFactory.fromMap(mapOf("validating" to true)))