mirror of
https://github.com/corda/corda.git
synced 2025-05-03 01:03:18 +00:00
CORDA-2647 - Prevent registration when previous state exists
This commit is contained in:
parent
b0cf41ef58
commit
4e9d1f1924
@ -1,6 +1,7 @@
|
|||||||
package net.corda.node.internal.subcommands
|
package net.corda.node.internal.subcommands
|
||||||
|
|
||||||
import net.corda.cliutils.CliWrapperBase
|
import net.corda.cliutils.CliWrapperBase
|
||||||
|
import net.corda.core.internal.VisibleForTesting
|
||||||
import net.corda.core.internal.createFile
|
import net.corda.core.internal.createFile
|
||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.exists
|
import net.corda.core.internal.exists
|
||||||
@ -12,10 +13,14 @@ import net.corda.node.internal.NodeStartupLogging.Companion.logger
|
|||||||
import net.corda.node.services.config.NodeConfiguration
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
import net.corda.node.utilities.registration.HTTPNetworkRegistrationService
|
||||||
import net.corda.node.utilities.registration.NodeRegistrationHelper
|
import net.corda.node.utilities.registration.NodeRegistrationHelper
|
||||||
|
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
|
||||||
import picocli.CommandLine.Mixin
|
import picocli.CommandLine.Mixin
|
||||||
import picocli.CommandLine.Option
|
import picocli.CommandLine.Option
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.lang.IllegalStateException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import java.sql.DriverManager
|
||||||
|
import java.sql.SQLException
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
class InitialRegistrationCli(val startup: NodeStartup): CliWrapperBase("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.") {
|
class InitialRegistrationCli(val startup: NodeStartup): CliWrapperBase("initial-registration", "Start initial node registration with Corda network to obtain certificate from the permissioning server.") {
|
||||||
@ -58,7 +63,12 @@ class InitialRegistration(val baseDirectory: Path, private val networkRootTrustS
|
|||||||
|
|
||||||
private val nodeRegistration = NodeRegistrationOption(networkRootTrustStorePath, networkRootTrustStorePassword)
|
private val nodeRegistration = NodeRegistrationOption(networkRootTrustStorePath, networkRootTrustStorePassword)
|
||||||
|
|
||||||
private fun registerWithNetwork(conf: NodeConfiguration) {
|
private val EXISTING_STATE_GENERIC_WARNING = "Please ensure there is no state from previous runs, before initiating registration of a node."
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun registerWithNetwork(conf: NodeConfiguration) {
|
||||||
|
verifyNoStateFromPreviousRuns(conf)
|
||||||
|
|
||||||
val versionInfo = startup.getVersionInfo()
|
val versionInfo = startup.getVersionInfo()
|
||||||
|
|
||||||
println("\n" +
|
println("\n" +
|
||||||
@ -82,10 +92,35 @@ class InitialRegistration(val baseDirectory: Path, private val networkRootTrustS
|
|||||||
println("Corda node will now terminate.")
|
println("Corda node will now terminate.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun verifyNoStateFromPreviousRuns(conf: NodeConfiguration) {
|
||||||
|
val artemisDirectory = baseDirectory / "artemis"
|
||||||
|
check(!artemisDirectory.exists()) { "The node folder contains an artemis directory. $EXISTING_STATE_GENERIC_WARNING" }
|
||||||
|
val brokersDirectory = baseDirectory / "brokers"
|
||||||
|
check(!brokersDirectory.exists()) { "There node folder contains a brokers directory. $EXISTING_STATE_GENERIC_WARNING" }
|
||||||
|
|
||||||
|
val datasourceProps = conf.dataSourceProperties
|
||||||
|
if (datasourceProps.isEmpty) throw IllegalStateException("There must be a database configured.")
|
||||||
|
val datasourceUrl = datasourceProps.getProperty("dataSource.url")
|
||||||
|
val datasourceUser = datasourceProps.getProperty("dataSource.user")
|
||||||
|
val datasourcePassword = datasourceProps.getProperty("dataSource.password")
|
||||||
|
|
||||||
|
try {
|
||||||
|
val connection = DriverManager.getConnection(datasourceUrl, datasourceUser, datasourcePassword)
|
||||||
|
val connectionMetadata = connection.metaData
|
||||||
|
// Accounting for different case-sensitivity behaviours (i.e. H2 creates tables in upper-case in some cases)
|
||||||
|
val tablesLowerCaseResultSet = connectionMetadata.getTables(null, null, "$NODE_DATABASE_PREFIX%", null)
|
||||||
|
val tablesUpperCaseResultSet = connectionMetadata.getTables(null, null, "${NODE_DATABASE_PREFIX.toUpperCase()}%", null)
|
||||||
|
check(!tablesLowerCaseResultSet.first() && !tablesUpperCaseResultSet.first()) {
|
||||||
|
"The database contains Corda-specific tables, while it should be empty. $EXISTING_STATE_GENERIC_WARNING"
|
||||||
|
}
|
||||||
|
} catch (exception: SQLException) {
|
||||||
|
throw Exception("An error occurred whilst connecting to \"$datasourceUrl\". ", exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initialRegistration(config: NodeConfiguration) {
|
private fun initialRegistration(config: NodeConfiguration) {
|
||||||
// Null checks for [compatibilityZoneURL], [rootTruststorePath] and [rootTruststorePassword] has been done in [CmdLineOptions.loadConfig]
|
// Null checks for [compatibilityZoneURL], [rootTruststorePath] and [rootTruststorePassword] has been done in [CmdLineOptions.loadConfig]
|
||||||
attempt { registerWithNetwork(config) }.doOnFailure(Consumer(this::handleRegistrationError)) as Try.Success
|
attempt { registerWithNetwork(config) }.doOnFailure(Consumer(this::handleRegistrationError))
|
||||||
// At this point the node registration was successful. We can delete the marker file.
|
|
||||||
deleteNodeRegistrationMarker(baseDirectory)
|
deleteNodeRegistrationMarker(baseDirectory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,98 @@
|
|||||||
|
package net.corda.node.internal.subcommands
|
||||||
|
|
||||||
|
import net.corda.core.internal.deleteRecursively
|
||||||
|
import net.corda.core.internal.div
|
||||||
|
import net.corda.node.internal.NodeStartup
|
||||||
|
import net.corda.node.services.config.NodeConfiguration
|
||||||
|
import net.corda.node.internal.Node
|
||||||
|
import org.h2.tools.Server
|
||||||
|
import org.junit.AfterClass
|
||||||
|
import org.junit.BeforeClass
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.mockito.Mockito.mock
|
||||||
|
import java.lang.IllegalStateException
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.sql.DriverManager
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class InitialRegistrationCliTest {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private lateinit var initialRegistration: InitialRegistration
|
||||||
|
|
||||||
|
private val networkTrustRootPassword = "some-password"
|
||||||
|
private val nodeStartup = mock(NodeStartup::class.java)
|
||||||
|
|
||||||
|
private val baseDirectory = Files.createTempDirectory("base-dir")!!
|
||||||
|
private val networkTrustRootFile = Files.createTempFile("trust-root-store", "jks")
|
||||||
|
|
||||||
|
private lateinit var nodeConfiguration: NodeConfiguration
|
||||||
|
private lateinit var datasourceProperties: Properties
|
||||||
|
private lateinit var node: Node
|
||||||
|
|
||||||
|
private val h2jdbcUrl = "jdbc:h2:tcp://localhost:10009/~/node"
|
||||||
|
private val h2User = "sa"
|
||||||
|
private val h2Password = ""
|
||||||
|
|
||||||
|
private lateinit var server: Server
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
@JvmStatic
|
||||||
|
fun prepare() {
|
||||||
|
nodeConfiguration = mock(NodeConfiguration::class.java)
|
||||||
|
datasourceProperties = Properties()
|
||||||
|
node = mock(Node::class.java)
|
||||||
|
|
||||||
|
Mockito.`when`(node.configuration).thenReturn(nodeConfiguration)
|
||||||
|
Mockito.`when`(nodeConfiguration.dataSourceProperties).thenReturn(datasourceProperties)
|
||||||
|
|
||||||
|
server = Server.createTcpServer("-tcpPort", "10009", "-tcpAllowOthers", "-tcpDaemon").start()
|
||||||
|
executeSqlStatement("CREATE TABLE NODE_ATTACHMENTS(USERNAME VARCHAR(20));")
|
||||||
|
|
||||||
|
initialRegistration = InitialRegistration(baseDirectory, networkTrustRootFile, networkTrustRootPassword, nodeStartup)
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
@JvmStatic
|
||||||
|
fun cleanup() {
|
||||||
|
baseDirectory.deleteRecursively()
|
||||||
|
networkTrustRootFile.deleteRecursively()
|
||||||
|
|
||||||
|
executeSqlStatement("DROP TABLE NODE_ATTACHMENTS;")
|
||||||
|
server.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun executeSqlStatement(sqlStatement: String) {
|
||||||
|
val connection = DriverManager.getConnection(h2jdbcUrl, h2User, h2Password)
|
||||||
|
val statement = connection.createStatement()
|
||||||
|
statement.execute(sqlStatement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun `registration fails when there is existing artemis folder`() {
|
||||||
|
Files.createDirectories(baseDirectory / "artemis")
|
||||||
|
|
||||||
|
initialRegistration.registerWithNetwork(node.configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun `registration fails when there is existing brokers folder`() {
|
||||||
|
Files.createDirectories(baseDirectory / "brokers")
|
||||||
|
|
||||||
|
initialRegistration.registerWithNetwork(node.configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun `registration fails when database contains tables`() {
|
||||||
|
datasourceProperties.setProperty("dataSource.url", h2jdbcUrl)
|
||||||
|
datasourceProperties.setProperty("dataSource.user", h2User)
|
||||||
|
datasourceProperties.setProperty("dataSource.password", h2Password)
|
||||||
|
Mockito.`when`(nodeConfiguration.dataSourceProperties).thenReturn(datasourceProperties)
|
||||||
|
|
||||||
|
initialRegistration.registerWithNetwork(node.configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user