From ea423215c239dec2afafeadd5bcc87c4e4cac9d9 Mon Sep 17 00:00:00 2001 From: Christian Sailer Date: Mon, 14 Sep 2020 11:25:18 +0100 Subject: [PATCH] ENT-5759 check for app schema migration (#6701) * Fix behaviour for missing schema outside devMode plus test --- node/build.gradle | 2 ++ .../persistence/DbSchemaInitialisationTest.kt | 29 +++++++++++++++++++ .../net/corda/node/internal/AbstractNode.kt | 6 ++-- settings.gradle | 1 + .../cordapps/missingmigration/build.gradle | 16 ++++++++++ .../MissingMigrationSchema.kt | 24 +++++++++++++++ .../missingmigrationcordapp/SimpleFlow.kt | 13 +++++++++ .../missingmigrationcordapp/TestEntity.kt | 16 ++++++++++ 8 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 testing/cordapps/missingmigration/build.gradle create mode 100644 testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/MissingMigrationSchema.kt create mode 100644 testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/SimpleFlow.kt create mode 100644 testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/TestEntity.kt diff --git a/node/build.gradle b/node/build.gradle index 5f5eb08b4d..fe895177f7 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -242,6 +242,8 @@ dependencies { slowIntegrationTestRuntime configurations.runtime slowIntegrationTestRuntime configurations.testRuntime + integrationTestCompile(project(":testing:cordapps:missingmigration")) + testCompile project(':testing:cordapps:dbfailure:dbfworkflows') } diff --git a/node/src/integration-test/kotlin/net/corda/node/persistence/DbSchemaInitialisationTest.kt b/node/src/integration-test/kotlin/net/corda/node/persistence/DbSchemaInitialisationTest.kt index 8c8de1a63e..101bf9c0f7 100644 --- a/node/src/integration-test/kotlin/net/corda/node/persistence/DbSchemaInitialisationTest.kt +++ b/node/src/integration-test/kotlin/net/corda/node/persistence/DbSchemaInitialisationTest.kt @@ -3,10 +3,16 @@ package net.corda.node.persistence import net.corda.core.utilities.getOrThrow import net.corda.node.flows.isQuasarAgentSpecified import net.corda.node.internal.ConfigurationException +import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException +import net.corda.nodeapi.internal.persistence.HibernateSchemaChangeException +import net.corda.testing.core.ALICE_NAME import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.NodeParameters import net.corda.testing.driver.driver +import net.corda.testing.node.TestCordapp import org.junit.Test +import net.corda.testing.node.internal.startNode +import org.assertj.core.api.Assertions.assertThatExceptionOfType import kotlin.test.assertFailsWith class DbSchemaInitialisationTest { @@ -19,4 +25,27 @@ class DbSchemaInitialisationTest { } } + @Test(timeout = 300_000) + fun `app migration resource is only mandatory when not in dev mode`() { + driver(DriverParameters(startNodesInProcess = true, + cordappsForAllNodes = emptyList(), + allowHibernateToManageAppSchema = false)) { + // in dev mode, it fails because the schema of our test CorDapp is missing + assertThatExceptionOfType(HibernateSchemaChangeException::class.java) + .isThrownBy { + startNode(NodeParameters(additionalCordapps = listOf(TestCordapp.findCordapp("net.corda.testing.missingmigrationcordapp")))).getOrThrow() + } + .withMessage("Incompatible schema change detected. Please run schema migration scripts (node with sub-command run-migration-scripts). Reason: Schema-validation: missing table [test_table]") + + // without devMode, it doesn't even get this far as it complains about the schema migration missing. + assertThatExceptionOfType(CouldNotCreateDataSourceException::class.java) + .isThrownBy { + startNode( + ALICE_NAME, + false, + NodeParameters(additionalCordapps = listOf(TestCordapp.findCordapp("net.corda.testing.missingmigrationcordapp")))).getOrThrow() + } + .withMessage("Could not create the DataSource: No migration defined for schema: net.corda.testing.missingmigrationcordapp.MissingMigrationSchema v1") + } + } } \ No newline at end of file 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 d7a563782f..6b1f04410d 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -469,9 +469,9 @@ abstract class AbstractNode(val configuration: NodeConfiguration, pendingCoreChanges = schemaMigration.getPendingChangesCount(schemaService.internalSchemas, true) } if(updateAppSchemas) { - schemaMigration.runMigration(!updateAppSchemasWithCheckpoints && haveCheckpoints, schemaService.appSchemas, false) + schemaMigration.runMigration(!updateAppSchemasWithCheckpoints && haveCheckpoints, schemaService.appSchemas, !configuration.devMode) } else { - pendingAppChanges = schemaMigration.getPendingChangesCount(schemaService.appSchemas, false) + pendingAppChanges = schemaMigration.getPendingChangesCount(schemaService.appSchemas, !configuration.devMode) } } // Now log the vendor string as this will also cause a connection to be tested eagerly. @@ -1023,7 +1023,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, database.startHikariPool(configuration.dataSourceProperties, metricRegistry) { dataSource, haveCheckpoints -> SchemaMigration(dataSource, cordappLoader, configuration.baseDirectory, configuration.myLegalName) .checkOrUpdate(schemaService.internalSchemas, runMigrationScripts, haveCheckpoints, true) - .checkOrUpdate(schemaService.appSchemas, runMigrationScripts, haveCheckpoints && !allowAppSchemaUpgradeWithCheckpoints, false) + .checkOrUpdate(schemaService.appSchemas, runMigrationScripts, haveCheckpoints && !allowAppSchemaUpgradeWithCheckpoints, !configuration.devMode) } /** Loads and starts a notary service if it is configured. */ diff --git a/settings.gradle b/settings.gradle index abcdd7c676..7416f3e599 100644 --- a/settings.gradle +++ b/settings.gradle @@ -101,6 +101,7 @@ include 'serialization-djvm:deserializers' include 'serialization-tests' include 'testing:cordapps:dbfailure:dbfcontracts' include 'testing:cordapps:dbfailure:dbfworkflows' +include 'testing:cordapps:missingmigration' // Common libraries - start include 'common-validation' diff --git a/testing/cordapps/missingmigration/build.gradle b/testing/cordapps/missingmigration/build.gradle new file mode 100644 index 0000000000..e004a34255 --- /dev/null +++ b/testing/cordapps/missingmigration/build.gradle @@ -0,0 +1,16 @@ +apply plugin: 'kotlin' +//apply plugin: 'net.corda.plugins.cordapp' +//apply plugin: 'net.corda.plugins.quasar-utils' + +dependencies { + compile project(":core") +} + +jar { + baseName "testing-missingmigration-cordapp" + manifest { + // This JAR is part of Corda's testing framework. + // Driver will not include it as part of an out-of-process node. + attributes('Corda-Testing': true) + } +} \ No newline at end of file diff --git a/testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/MissingMigrationSchema.kt b/testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/MissingMigrationSchema.kt new file mode 100644 index 0000000000..3bb32576de --- /dev/null +++ b/testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/MissingMigrationSchema.kt @@ -0,0 +1,24 @@ +package net.corda.testing.missingmigrationcordapp + +import net.corda.core.schemas.MappedSchema +import net.corda.core.schemas.PersistentState +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.Table + +object MissingMigrationSchema + +object MissingMigrationSchemaV1 : MappedSchema( + schemaFamily = MissingMigrationSchema.javaClass, + version = 1, + mappedTypes = listOf(MissingMigrationSchemaV1.TestEntity::class.java)) { + + @Entity + @Table(name = "test_table") + class TestEntity( + @Column(name = "random_value") + var randomValue: String + ) : PersistentState() { + constructor() : this("") + } +} \ No newline at end of file diff --git a/testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/SimpleFlow.kt b/testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/SimpleFlow.kt new file mode 100644 index 0000000000..f1c8c6534a --- /dev/null +++ b/testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/SimpleFlow.kt @@ -0,0 +1,13 @@ +package net.corda.testing.missingmigrationcordapp + +import net.corda.core.flows.FlowLogic +import net.corda.core.flows.InitiatingFlow +import net.corda.core.flows.StartableByRPC + +@StartableByRPC +@InitiatingFlow +class SimpleFlow : FlowLogic() { + override fun call() { + logger.info("Running simple flow doing nothing") + } +} \ No newline at end of file diff --git a/testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/TestEntity.kt b/testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/TestEntity.kt new file mode 100644 index 0000000000..5d261a0781 --- /dev/null +++ b/testing/cordapps/missingmigration/src/main/kotlin/net/corda/testing/missingmigrationcordapp/TestEntity.kt @@ -0,0 +1,16 @@ +package net.corda.testing.missingmigrationcordapp + +import net.corda.core.identity.AbstractParty +import net.corda.core.schemas.MappedSchema +import net.corda.core.schemas.PersistentState +import net.corda.core.schemas.QueryableState + +class TestEntity(val randomValue: String, override val participants: List) : QueryableState { + override fun supportedSchemas(): Iterable { + return listOf(MissingMigrationSchemaV1) + } + + override fun generateMappedObject(schema: MappedSchema): PersistentState { + return MissingMigrationSchemaV1.TestEntity(randomValue) + } +} \ No newline at end of file