Skip to content

Commit a2b285f

Browse files
committed
copy database to another instance
1 parent bbaef5f commit a2b285f

File tree

6 files changed

+87
-20
lines changed

6 files changed

+87
-20
lines changed

modules/fbs-runner/checker/src/main/resources/config.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,10 @@
2121
"SQL_PLAYGROUND_PSQL_SERVER_PASSWORD": "SqfyBWhiFGr7FK60cVR2rel",
2222
"SQL_PLAYGROUND_PSQL_SERVER_URL": "jdbc:postgresql://localhost:5432?allowMultiQueries=true",
2323
"SQL_QUERY_TIMEOUT_S": 10,
24-
"SQL_MAX_IDLE_TIME": 10
24+
"SQL_MAX_IDLE_TIME": 10,
25+
"PSQL_SHARING_SERVER_HOST": "psql-sharing",
26+
"PSQL_SHARING_SERVER_PORT": "5444",
27+
"PSQL_SHARING_SERVER_URL": "jdbc:postgresql://psql-sharing:5444",
28+
"PSQL_SHARING_SERVER_USERNAME": "postgres",
29+
"PSQL_SHARING_SERVER_PASSWORD": "R$7pWqY@K5zE3Xt&g9L1MfD"
2530
}

modules/fbs-runner/checker/src/main/scala/de/thm/ii/fbs/services/db/DBOperationsService.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ abstract case class DBOperationsService(dbName: String, username: String, queryT
1818
def initDB(client: JDBCClient, query: String): Future[ResultSet] = {
1919
queryFutureWithTimeout(client, query)
2020
}
21+
/**
2122
def copyDatabase(targetClient: JDBCClient, targetDBName: String): Future[ResultSet]
22-
23-
def createUserWithAccess(targetClient: JDBCClient, targetDBName: String): Future[String]
24-
23+
*/
24+
def createUserWithAccess(targetClient: JDBCClient, targetDBName: String, targetHost: String): Future[String]
25+
def copyDatabaseToAnotherInstance(sourceHost: String, sourceDBName: String, targetHost: String, targetDBName: String): Future[Unit]
2526
def createUserWithWriteAccess(client: JDBCClient, skipUserCreation: Boolean = false): Future[String]
2627

2728
def createUserIfNotExist(client: SQLConnection, password: String): Future[ResultSet]

modules/fbs-runner/checker/src/main/scala/de/thm/ii/fbs/services/db/PsqlOperationsService.scala

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import de.thm.ii.fbs.types.PsqlPrivileges
44
import io.vertx.lang.scala.json.JsonArray
55
import io.vertx.scala.ext.jdbc.JDBCClient
66
import io.vertx.scala.ext.sql.{ResultSet, SQLConnection}
7+
import scala.sys.process._
78

89
import scala.concurrent.ExecutionContext.Implicits.global
910
import scala.concurrent.Future
11+
import scala.sys.process.Process
1012

1113
class PsqlOperationsService(override val dbName: String, override val username: String, override val queryTimeout: Int)
1214
extends DBOperationsService(dbName, username, queryTimeout) {
@@ -152,19 +154,39 @@ class PsqlOperationsService(override val dbName: String, override val username:
152154

153155
client.queryFuture(s"$tables $constrains $views $routines $triggers")
154156
}
157+
/**
155158
override def copyDatabase(targetClient: JDBCClient, targetDBName: String): Future[ResultSet] = {
156159
val copyDBQuery = s"CREATE DATABASE $targetDBName WITH TEMPLATE $dbName"
157160
targetClient.queryFuture(copyDBQuery)
158161
}
162+
*/
163+
def copyDatabaseToAnotherInstance(sourceHost: String, sourceDBName: String, targetHost: String, targetDBName: String): Future[Unit] = Future {
164+
/**Command to dump the database from the source
165+
* missing case where sourcehost has no password*/
166+
val dumpCommand = s"pg_dump -h $sourceHost -U $username -F c -b -v -f /tmp/$sourceDBName.dump $sourceDBName"
167+
val dumpExitCode = Process(Seq("bash", "-c", dumpCommand), None, "PGPASSWORD" -> "your_source_db_password").!
168+
169+
if (dumpExitCode != 0) {
170+
throw new Exception(s"Failed to dump the database from $sourceHost")
171+
}
172+
173+
// Command to restore the dump to the target
174+
val restoreCommand = s"pg_restore -h $targetHost -U $username -d $targetDBName -v /tmp/$sourceDBName.dump"
175+
val restoreExitCode = Process(Seq("bash", "-c", restoreCommand), None, "PGPASSWORD" -> "your_target_db_password").!
176+
177+
if (restoreExitCode != 0) {
178+
throw new Exception(s"Failed to restore the database to $targetHost")
179+
}
180+
}
159181

160-
override def createUserWithAccess(targetClient: JDBCClient, targetDBName: String): Future[String] = {
182+
override def createUserWithAccess(targetClient: JDBCClient, targetDBName: String, targetHost: String): Future[String] = {
161183
val password = generateUserPassword()
162184
val userCreateQuery = s"CREATE USER $username WITH PASSWORD '$password';"
163185
val permissionsQuery = s"GRANT ALL PRIVILEGES ON DATABASE $targetDBName TO $username;"
164186

165187
for {
166188
_ <- targetClient.queryFuture(userCreateQuery)
167189
_ <- targetClient.queryFuture(permissionsQuery)
168-
} yield s"postgresql://$username:$password@<target-host>/$targetDBName"
190+
} yield s"postgresql://$username:$password@$targetHost/$targetDBName"
169191
}
170192
}

modules/fbs-runner/checker/src/main/scala/de/thm/ii/fbs/services/runner/SQLPlaygroundService.scala

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import de.thm.ii.fbs.types.{OutputJsonStructure, SqlPlaygroundRunArgs}
66
import de.thm.ii.fbs.util.{DBTypes, DatabaseInformationService, PlaygroundDBConnections, SqlPlaygroundMode}
77
import io.vertx.core.json.JsonObject
88
import io.vertx.scala.ext.sql.ResultSet
9-
9+
import io.vertx.scala.ext.jdbc.JDBCClient
1010
import scala.concurrent.ExecutionContext.Implicits.global
1111
import scala.concurrent.Future
1212
import scala.util.{Failure, Success}
@@ -21,6 +21,7 @@ object SQLPlaygroundService {
2121
OutputJsonStructure("triggers", Option("manipulation"))
2222
)
2323

24+
2425
def isPlaygroundResult(res: JsonObject): Boolean = res.getString("resultType", "").equals(PLAYGROUND_RESULT_TYPE)
2526

2627
/**
@@ -70,6 +71,13 @@ class SQLPlaygroundService(val sqlRunArgs: SqlPlaygroundRunArgs, val con: Playgr
7071
new PsqlOperationsService(dbName, username, queryTimeout)
7172
}
7273

74+
def copyDBAndCreateUser(sourceHost: String, targetHost: String, targetClient: JDBCClient): Future[String] = {
75+
val dbOperations = initDBOperations()
76+
for {
77+
_ <- dbOperations.copyDatabaseToAnotherInstance(sourceHost, dbOperations.dbName, targetHost, dbOperations.dbName)
78+
uri <- dbOperations.createUserWithAccess(targetClient, dbOperations.dbName, targetHost)
79+
} yield uri
80+
}
7381
def executeStatement(): Future[(ResultSet, ResultSet)] = {
7482
val dbOperations = initDBOperations()
7583
val deleteDatabase = SqlPlaygroundMode.shouldDeleteDatabase(sqlRunArgs.mode)

modules/fbs-runner/checker/src/main/scala/de/thm/ii/fbs/services/runner/SQLRunnerService.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ class SQLRunnerService(val sqlRunArgs: SqlRunArgs, val solutionCon: DBConnection
141141
dbOperation
142142
}
143143

144+
/**
144145
def copyPlaygroundDBAndCreateUser(targetDBHost: String, targetDBPort: Int, dbUser: String, dbPassword: String, originalDBName: String, targetDBName: String): Future[String] = {
145146
// Create a client for the target database
146147
val targetJDBCClient = JDBCClient.createNonShared(vertx, new JsonObject()
@@ -164,7 +165,7 @@ class SQLRunnerService(val sqlRunArgs: SqlRunArgs, val solutionCon: DBConnection
164165
s"jdbc:postgresql://$targetDBHost:$targetDBPort/$targetDBName?user=$newUserAndPassword._1&password=$newUserAndPassword._2"
165166
}
166167
}
167-
168+
*/
168169
private def buildName(nameExtension: String): String = s"${sqlRunArgs.submissionId}_${sqlRunArgs.runnerId}_$nameExtension"
169170

170171
/**
@@ -304,7 +305,7 @@ class SQLRunnerService(val sqlRunArgs: SqlRunArgs, val solutionCon: DBConnection
304305
(msg, success, correctResults)
305306
}
306307

307-
/** def copyPlaygroundDBAndCreateUser(targetDBHost: String, targetDBPort: Int): Future[String] = {
308+
/** def copyPlaygroundDBAndCreateUser(targetDBHost: String, targetDBPort: Int): Future[String] = {
308309
val targetDBName = s"${dbName}_copy"
309310
val targetJDBCClient = JDBCClient.createNonShared(vertx, new JsonObject()
310311
.put("url", s"jdbc:postgresql://$targetDBHost:$targetDBPort/")

modules/fbs-runner/checker/src/main/scala/de/thm/ii/fbs/verticles/runner/SqlPlaygroundVerticle.scala

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package de.thm.ii.fbs.verticles.runner
22

3-
import de.thm.ii.fbs.services.runner.SQLPlaygroundService
3+
import de.thm.ii.fbs.services.db.PsqlOperationsService
4+
import de.thm.ii.fbs.services.runner.{SQLPlaygroundService}
45
import de.thm.ii.fbs.types._
56
import de.thm.ii.fbs.util.DBTypes.PSQL_CONFIG_KEY
67
import de.thm.ii.fbs.util.PlaygroundDBConnections
@@ -16,27 +17,27 @@ import scala.concurrent.Future
1617
import scala.util.{Failure, Success}
1718

1819
/**
19-
* Object that stores all static vars for the SqlPlaygroundVerticle
20-
*/
20+
* Object that stores all static vars for the SqlPlaygroundVerticle
21+
*/
2122
object SqlPlaygroundVerticle {
2223
/** Event Bus Address to start an runner */
2324
val RUN_ADDRESS = "de.thm.ii.fbs.runner.sqlPlayground"
2425
}
2526

2627
/**
27-
* Verticle that starts the SqlPlayground
28-
*
29-
* @author Max Stephan
30-
*/
28+
* Verticle that starts the SqlPlayground
29+
*
30+
* @author Max Stephan
31+
*/
3132
class SqlPlaygroundVerticle extends ScalaVerticle {
3233
private val logger = ScalaLogger.getLogger(this.getClass.getName)
3334
private var sqlPools = Map[String, SqlPoolWithConfig]()
3435

3536
/**
36-
* start SqlRunnerVerticle
37-
*
38-
* @return vertx Future
39-
*/
37+
* start SqlRunnerVerticle
38+
*
39+
* @return vertx Future
40+
*/
4041
override def startFuture(): Future[_] = {
4142
val psqlDataSource = s"${PSQL_CONFIG_KEY}_playground"
4243
val psqlConfig = new JsonObject()
@@ -53,6 +54,30 @@ class SqlPlaygroundVerticle extends ScalaVerticle {
5354
vertx.eventBus().consumer(RUN_ADDRESS, startSqlPlayground).completionFuture()
5455
}
5556

57+
private def copyDatabaseAndCreateUser(runArgs: SqlPlaygroundRunArgs): Future[String] = {
58+
// Extract sqlRunArgs from the message
59+
60+
61+
// Get the PlaygroundDBConnections
62+
val con = getConnection(runArgs).getOrElse(throw new Exception("Failed to get database connection"))
63+
64+
// Get the query timeout from config
65+
val queryTimeout = config.getInteger("SQL_QUERY_TIMEOUT_S", 10)
66+
67+
// Create an instance of SQLPlaygroundService
68+
val sqlPlaygroundService = new SQLPlaygroundService(runArgs, con, queryTimeout)
69+
70+
// Extract source and target host
71+
val sourceHost = config.getString("PSQL_SERVER_URL", "jdbc:postgresql://localhost:5432").split("//")(1).split("/")(0)
72+
val targetHost = config.getString("PSQL_SHARING_SERVER_URL", "jdbc:postgresql://psql-sharing:5432").split("//")(1).split("/")(0)
73+
74+
// Get the JDBC client for the target database
75+
val targetClient = sqlPools.getOrElse(PSQL_CONFIG_KEY, throw new Exception("PSQL config not found")).pool
76+
77+
// Call the method to copy database and create user
78+
sqlPlaygroundService.copyDBAndCreateUser(sourceHost, targetHost, targetClient)
79+
}
80+
5681
private def startSqlPlayground(msg: Message[JsonObject]): Future[Unit] = Future {
5782
val runArgs = msg.body().mapTo(classOf[SqlPlaygroundRunArgs])
5883

@@ -63,6 +88,11 @@ class SqlPlaygroundVerticle extends ScalaVerticle {
6388

6489
if (con.isDefined) {
6590
executeQueries(runArgs, con.get)
91+
// Call the method to copy database and create user
92+
copyDatabaseAndCreateUser(runArgs).onComplete {
93+
case Success(uri) => logger.info(s"Database copied, new URI: $uri")
94+
case Failure(ex) => logger.error("Error in copying database and creating user", ex)
95+
}
6696
}
6797
} catch {
6898
case e: Throwable => handleError(runArgs, e)

0 commit comments

Comments
 (0)