From 26cfea202b5f259de861a93bd051ce24b55aad02 Mon Sep 17 00:00:00 2001 From: szymonsztuka Date: Thu, 17 Jan 2019 17:26:24 +0000 Subject: [PATCH] CORDA-2450 Creating attachment version from whitelisted JARs fails for node upgrade (#4593) Upgrade from node 3.0 to 4.0 fails to create versions of whitelisted JARs from networkParameters - read parameters from file at first as in Corda 3.0 there no relevant table, then try from the table. As this is migration, the code will run only once on each node, she increased log level to info for messages. Tested using https://r3-cev.atlassian.net/browse/R3T-1549 --- .../AttachmentVersionNumberMigration.kt | 60 +++++++++++-------- .../internal/persistence/SchemaMigration.kt | 7 ++- .../net/corda/node/internal/AbstractNode.kt | 7 ++- 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/AttachmentVersionNumberMigration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/AttachmentVersionNumberMigration.kt index c005100464..8a887dd99f 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/AttachmentVersionNumberMigration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/AttachmentVersionNumberMigration.kt @@ -5,10 +5,16 @@ import liquibase.database.Database import liquibase.database.jvm.JdbcConnection import liquibase.exception.ValidationErrors import liquibase.resource.ResourceAccessor +import net.corda.core.internal.div +import net.corda.core.internal.readObject import net.corda.core.node.NetworkParameters -import net.corda.core.node.services.AttachmentId import net.corda.core.serialization.deserialize import net.corda.core.utilities.contextLogger +import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME +import net.corda.nodeapi.internal.network.SignedNetworkParameters +import net.corda.nodeapi.internal.persistence.SchemaMigration.Companion.NODE_BASE_DIR_KEY +import java.nio.file.Path +import java.nio.file.Paths class AttachmentVersionNumberMigration : CustomTaskChange { companion object { @@ -17,40 +23,49 @@ class AttachmentVersionNumberMigration : CustomTaskChange { override fun execute(database: Database?) { val connection = database?.connection as JdbcConnection + val msg = "Attachment version creation from whitelisted JARs" + try { - logger.debug("Start executing...") - val networkParameters = getNetworkParameters(connection) - if (networkParameters == null) { - logger.debug("Network parameters not found.") - return + logger.info("Start executing...") + var networkParameters: NetworkParameters? + + if (System.getProperty(NODE_BASE_DIR_KEY).isNotEmpty()) { + val path = Paths.get(System.getProperty(NODE_BASE_DIR_KEY)) / NETWORK_PARAMS_FILE_NAME + networkParameters = getNetworkParametersFromFile(path) + if (networkParameters != null) { + logger.info("$msg using network parameters from $path, whitelistedContractImplementations: ${networkParameters.whitelistedContractImplementations}.") + } else { + logger.warn("$msg skipped, network parameters not found in $path.") + return + } } else { - logger.debug("Network parameters epoch: ${networkParameters.epoch}, whitelistedContractImplementations: ${networkParameters.whitelistedContractImplementations}.") + logger.error("$msg skipped, network parameters not retrieved, could not determine node base directory due to system property $NODE_BASE_DIR_KEY being not set.") + return } + val availableAttachments = getAttachmentsWithDefaultVersion(connection) if (availableAttachments.isEmpty()) { - logger.debug("Attachments not found.") + logger.info("$msg skipped, no attachments not found.") return } else { - logger.debug("Attachments with version '1': $availableAttachments") + logger.info("$msg, candidate attachments with version '1': $availableAttachments") } availableAttachments.forEach { attachmentId -> - val versions = networkParameters?.whitelistedContractImplementations?.values.mapNotNull { it.indexOfFirst { it.toString() == attachmentId} }.filter { it >= 0 } + val versions = networkParameters?.whitelistedContractImplementations?.values.mapNotNull { it.indexOfFirst { it.toString() == attachmentId } }.filter { it >= 0 } val maxPosition = versions.max() ?: 0 if (maxPosition > 0) { val version = maxPosition + 1 - val msg = "Updating version of attachment $attachmentId to '$version'" + val msg = "Updating version of attachment $attachmentId to '$version'." if (versions.toSet().size > 1) logger.warn("Several versions based on whitelistedContractImplementations position are available: ${versions.toSet()}. $msg") else - logger.debug(msg) + logger.info(msg) updateVersion(connection, attachmentId, version) } } - - logger.debug("Done") } catch (e: Exception) { - logger.error("Exception while retrieving network parameters ${e.message}", e) + logger.error("$msg exception ${e.message}", e) } } @@ -68,17 +83,10 @@ class AttachmentVersionNumberMigration : CustomTaskChange { override fun setUp() { } - private fun getNetworkParameters(connection: JdbcConnection): NetworkParameters? = - connection.createStatement().use { - val rs = it.executeQuery("SELECT PARAMETERS_BYTES FROM NODE_NETWORK_PARAMETERS ORDER BY EPOCH DESC") - if (rs.next()) { - val networkParametersBytes = rs.getBytes(1) as ByteArray - val networkParameters: NetworkParameters = networkParametersBytes.deserialize() - rs.close() - networkParameters - } else - null - } + private fun getNetworkParametersFromFile(path: Path): NetworkParameters? { + val networkParametersBytes = path?.readObject() + return networkParametersBytes?.raw?.deserialize() + } private fun getAttachmentsWithDefaultVersion(connection: JdbcConnection): List = connection.createStatement().use { 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 b78748b3da..bd51726f76 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 @@ -13,6 +13,7 @@ import net.corda.core.schemas.MappedSchema import net.corda.core.utilities.contextLogger import java.io.ByteArrayInputStream import java.io.InputStream +import java.nio.file.Path import java.sql.Statement import javax.sql.DataSource @@ -20,10 +21,12 @@ class SchemaMigration( val schemas: Set, val dataSource: DataSource, private val databaseConfig: DatabaseConfig, - private val classLoader: ClassLoader = Thread.currentThread().contextClassLoader) { + private val classLoader: ClassLoader = Thread.currentThread().contextClassLoader, + private val currentDirectory: Path?) { companion object { private val logger = contextLogger() + const val NODE_BASE_DIR_KEY = "liquibase.nodeDaseDir" } /** @@ -86,6 +89,8 @@ class SchemaMigration( } } + System.setProperty(NODE_BASE_DIR_KEY, currentDirectory.toString()) // base dir for any custom change set which may need to load a file (currently AttachmentVersionNumberMigration) + val customResourceAccessor = CustomResourceAccessor(dynamicInclude, changelogList, classLoader) val liquibase = Liquibase(dynamicInclude, customResourceAccessor, getLiquibaseDatabase(JdbcConnection(connection))) 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 047cb623b3..a32f880b4c 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -90,6 +90,7 @@ import rx.Observable import rx.Scheduler import java.io.IOException import java.lang.reflect.InvocationTargetException +import java.nio.file.Path import java.nio.file.Paths import java.security.KeyPair import java.security.KeyStoreException @@ -778,7 +779,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, protected open fun startDatabase() { val props = configuration.dataSourceProperties if (props.isEmpty) throw DatabaseConfigurationException("There must be a database configured.") - database.startHikariPool(props, configuration.database, schemaService.internalSchemas(), metricRegistry, this.cordappLoader.appClassLoader) + database.startHikariPool(props, configuration.database, schemaService.internalSchemas(), metricRegistry, this.cordappLoader.appClassLoader, configuration.baseDirectory) // Now log the vendor string as this will also cause a connection to be tested eagerly. logVendorString(database, log) } @@ -1090,10 +1091,10 @@ fun createCordaPersistence(databaseConfig: DatabaseConfig, return CordaPersistence(databaseConfig, schemaService.schemaOptions.keys, jdbcUrl, cacheFactory, attributeConverters, customClassLoader) } -fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set, metricRegistry: MetricRegistry? = null, classloader: ClassLoader = Thread.currentThread().contextClassLoader) { +fun CordaPersistence.startHikariPool(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set, metricRegistry: MetricRegistry? = null, classloader: ClassLoader = Thread.currentThread().contextClassLoader, currentDir: Path? = null) { try { val dataSource = DataSourceFactory.createDataSource(hikariProperties, metricRegistry = metricRegistry) - val schemaMigration = SchemaMigration(schemas, dataSource, databaseConfig, classloader) + val schemaMigration = SchemaMigration(schemas, dataSource, databaseConfig, classloader, currentDir) schemaMigration.nodeStartup(dataSource.connection.use { DBCheckpointStorage().getCheckpointCount(it) != 0L }) start(dataSource) } catch (ex: Exception) {