From c82cd26b4b01395160a505199bae4c98f5de751d Mon Sep 17 00:00:00 2001 From: Hongchol Sinn Date: Fri, 5 Dec 2025 16:37:02 -0800 Subject: [PATCH] feat: Add profile for Aurora database --- applications/credhub-api/build.gradle | 1 + .../main/resources/application-dev-aurora.yml | 22 ++++++ .../application-unit-test-aurora.yml | 14 ++++ backends/credhub/build.gradle | 1 + .../shared-backend-configuration/build.gradle | 1 + build.gradle | 3 +- components/auth/build.gradle | 1 + components/credentials/build.gradle | 1 + components/encryption/build.gradle | 1 + components/permissions/build.gradle | 1 + ...rallelAuroraTestDataSourceConfiguration.kt | 78 +++++++++++++++++++ scripts/run_tests_aurora.sh | 58 ++++++++++++++ 12 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 applications/credhub-api/src/main/resources/application-dev-aurora.yml create mode 100644 applications/credhub-api/src/test/resources/application-unit-test-aurora.yml create mode 100644 components/test-support/src/test/kotlin/org/cloudfoundry/credhub/config/ParallelAuroraTestDataSourceConfiguration.kt create mode 100755 scripts/run_tests_aurora.sh diff --git a/applications/credhub-api/build.gradle b/applications/credhub-api/build.gradle index 4eaa58785..7f146812d 100644 --- a/applications/credhub-api/build.gradle +++ b/applications/credhub-api/build.gradle @@ -63,6 +63,7 @@ dependencies { // DB implementation("org.postgresql:postgresql") implementation("org.mariadb.jdbc:mariadb-java-client:${mariadbJdbcVersion}") + runtimeOnly("software.amazon.jdbc:aws-advanced-jdbc-wrapper:${awsAdvancedJdbcVersion}") implementation("org.flywaydb:flyway-core:${flywayVersion}") implementation("org.flywaydb:flyway-mysql:${flywayVersion}") implementation("org.flywaydb:flyway-database-postgresql:${flywayVersion}") diff --git a/applications/credhub-api/src/main/resources/application-dev-aurora.yml b/applications/credhub-api/src/main/resources/application-dev-aurora.yml new file mode 100644 index 000000000..0702057bc --- /dev/null +++ b/applications/credhub-api/src/main/resources/application-dev-aurora.yml @@ -0,0 +1,22 @@ +spring: + datasource: + username: ${AURORA_DB_USERNAME:root} + password: ${AURORA_DB_PASSWORD:} + url: jdbc:aws-wrapper:mariadb://${AURORA_DB_HOST_PORT:localhost:3306}/credhub_dev + + hikari: + # TODO: Cursor-generated. Review. + # Connection pool settings optimized for Aurora + maximum-pool-size: 20 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + leak-detection-threshold: 60000 + flyway: + locations: classpath:/db/migration/common,classpath:/db/migration/mysql + jpa: + properties: + hibernate: + # Needed for hibernate to work with aws-wrapper driver + dialect: org.hibernate.dialect.MariaDBDialect diff --git a/applications/credhub-api/src/test/resources/application-unit-test-aurora.yml b/applications/credhub-api/src/test/resources/application-unit-test-aurora.yml new file mode 100644 index 000000000..9c430b0fa --- /dev/null +++ b/applications/credhub-api/src/test/resources/application-unit-test-aurora.yml @@ -0,0 +1,14 @@ +spring: + datasource: + # Override these with environment variables for testing: + # AURORA_TEST_DB_URL, AURORA_TEST_DB_USERNAME, AURORA_TEST_DB_PASSWORD + username: ${AURORA_TEST_DB_USERNAME:root} + password: ${AURORA_TEST_DB_PASSWORD:} + url: jdbc:aws-wrapper:mariadb://${AURORA_DB_HOST:localhost}:${AURORA_DB_PORT:3306}/credhub_test + flyway: + locations: classpath:/db/migration/common,classpath:/db/migration/mysql + jpa: + properties: + hibernate: + # Needed for hibernate to work with aws-wrapper driver + dialect: org.hibernate.dialect.MariaDBDialect diff --git a/backends/credhub/build.gradle b/backends/credhub/build.gradle index 5391d724d..fe1008ef3 100644 --- a/backends/credhub/build.gradle +++ b/backends/credhub/build.gradle @@ -73,6 +73,7 @@ dependencies { implementation("org.flywaydb:flyway-mysql:${flywayVersion}") implementation("org.flywaydb:flyway-database-postgresql:${flywayVersion}") implementation("org.mariadb.jdbc:mariadb-java-client:${mariadbJdbcVersion}") + runtimeOnly("software.amazon.jdbc:aws-advanced-jdbc-wrapper:${awsAdvancedJdbcVersion}") implementation("com.h2database:h2") // Other diff --git a/backends/shared-backend-configuration/build.gradle b/backends/shared-backend-configuration/build.gradle index fb1ccc99a..011a2dd83 100644 --- a/backends/shared-backend-configuration/build.gradle +++ b/backends/shared-backend-configuration/build.gradle @@ -37,6 +37,7 @@ dependencies { implementation("org.postgresql:postgresql") implementation("com.h2database:h2") implementation("org.mariadb.jdbc:mariadb-java-client:${mariadbJdbcVersion}") + runtimeOnly("software.amazon.jdbc:aws-advanced-jdbc-wrapper:${awsAdvancedJdbcVersion}") // Other diff --git a/build.gradle b/build.gradle index 850439beb..c4929dbde 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,8 @@ buildscript { ktlintVersion = '1.5.0' passayVersion = '1.6.6' springBootVersion = '3.5.8' - mariadbJdbcVersion = '2.7.12' // Bumping to v3 breaks some pipeline jobs, so pinning to v2 for now. v2 (current version) is stable and will be supported until about September 2025 (https://mariadb.com/kb/en/about-mariadb-connector-j/). + mariadbJdbcVersion = '3.5.6' + awsAdvancedJdbcVersion = '2.6.7' grpcVersion = '1.77.0' // We need to stay with protobuf-java & protoc 3.25.x as the latest // grpc still depends on protobuf-java & protoc 3.25.x. Once we diff --git a/components/auth/build.gradle b/components/auth/build.gradle index 21ce9bb89..3e20b96b0 100644 --- a/components/auth/build.gradle +++ b/components/auth/build.gradle @@ -36,6 +36,7 @@ dependencies { testImplementation("org.flywaydb:flyway-database-postgresql:${flywayVersion}") implementation("org.postgresql:postgresql") implementation("org.mariadb.jdbc:mariadb-java-client:${mariadbJdbcVersion}") + runtimeOnly("software.amazon.jdbc:aws-advanced-jdbc-wrapper:${awsAdvancedJdbcVersion}") implementation("com.h2database:h2") // Other diff --git a/components/credentials/build.gradle b/components/credentials/build.gradle index 2eb4cb149..4ea94a30e 100644 --- a/components/credentials/build.gradle +++ b/components/credentials/build.gradle @@ -62,6 +62,7 @@ dependencies { implementation("org.flywaydb:flyway-database-postgresql:${flywayVersion}") implementation("org.postgresql:postgresql") implementation("org.mariadb.jdbc:mariadb-java-client:${mariadbJdbcVersion}") + runtimeOnly("software.amazon.jdbc:aws-advanced-jdbc-wrapper:${awsAdvancedJdbcVersion}") implementation("com.h2database:h2") // proto diff --git a/components/encryption/build.gradle b/components/encryption/build.gradle index 4d7e025ff..8733526c8 100644 --- a/components/encryption/build.gradle +++ b/components/encryption/build.gradle @@ -56,6 +56,7 @@ dependencies { testImplementation("org.flywaydb:flyway-database-postgresql:${flywayVersion}") implementation("org.postgresql:postgresql") implementation("org.mariadb.jdbc:mariadb-java-client:${mariadbJdbcVersion}") + runtimeOnly("software.amazon.jdbc:aws-advanced-jdbc-wrapper:${awsAdvancedJdbcVersion}") implementation("com.h2database:h2") // Other diff --git a/components/permissions/build.gradle b/components/permissions/build.gradle index 8656aff59..f9188b4a4 100644 --- a/components/permissions/build.gradle +++ b/components/permissions/build.gradle @@ -36,6 +36,7 @@ dependencies { implementation("org.postgresql:postgresql") implementation("com.h2database:h2") implementation("org.mariadb.jdbc:mariadb-java-client:${mariadbJdbcVersion}") + runtimeOnly("software.amazon.jdbc:aws-advanced-jdbc-wrapper:${awsAdvancedJdbcVersion}") // Other diff --git a/components/test-support/src/test/kotlin/org/cloudfoundry/credhub/config/ParallelAuroraTestDataSourceConfiguration.kt b/components/test-support/src/test/kotlin/org/cloudfoundry/credhub/config/ParallelAuroraTestDataSourceConfiguration.kt new file mode 100644 index 000000000..aa8a11125 --- /dev/null +++ b/components/test-support/src/test/kotlin/org/cloudfoundry/credhub/config/ParallelAuroraTestDataSourceConfiguration.kt @@ -0,0 +1,78 @@ +package org.cloudfoundry.credhub.config + +import org.springframework.boot.jdbc.DataSourceBuilder +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Primary +import org.springframework.context.annotation.Profile +import org.springframework.core.env.Environment +import org.springframework.jdbc.core.JdbcTemplate +import java.sql.ResultSet +import javax.sql.DataSource + +@Profile("unit-test-aurora") +@Configuration +class ParallelAuroraTestDataSourceConfiguration( + private val environment: Environment, +) { + private fun getGradleWorkerId(): String = System.getProperty("org.gradle.test.worker") + + private fun createTestDatabaseForWorker(workerId: String) { + val workerDatabaseName = "credhub_test_$workerId" + val baseUrl = environment.getProperty("spring.datasource.url", "jdbc:aws-wrapper:mariadb://localhost:3306/credhub_test") + val username = environment.getProperty("spring.datasource.username", "root") + val password = environment.getProperty("spring.datasource.password", "") + + // Extract base URL without database name + val urlWithoutDb = baseUrl.substringBeforeLast("/") + + val tempDataSource = + DataSourceBuilder + .create() + .url(urlWithoutDb) + .username(username) + .password(password) + .build() + + val jdbcTemplate = JdbcTemplate(tempDataSource) + val noDb = + jdbcTemplate + .query( + "SELECT 1 from INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?;", + { rs: ResultSet, _: Int -> rs.getBoolean(1) }, + workerDatabaseName, + ).isEmpty() + + if (noDb) { + jdbcTemplate.execute("CREATE DATABASE $workerDatabaseName") + } + + tempDataSource.connection.close() + } + + @Primary + @Bean(name = ["dataSource"]) + fun getParallelTestDataSource(): DataSource { + val workerId = getGradleWorkerId() + + createTestDatabaseForWorker(workerId) + + val baseUrl = environment.getProperty("spring.datasource.url", "jdbc:aws-wrapper:mariadb://localhost:3306/credhub_test") + val username = environment.getProperty("spring.datasource.username", "root") + val password = environment.getProperty("spring.datasource.password", "") + + // Extract base URL without database name and append worker-specific database + val urlWithoutDb = baseUrl.substringBeforeLast("/") + val urlWithWorkerDb = "$urlWithoutDb/credhub_test_$workerId" + + val dataSource = + DataSourceBuilder + .create() + .url(urlWithWorkerDb) + .username(username) + .password(password) + .build() + + return dataSource + } +} diff --git a/scripts/run_tests_aurora.sh b/scripts/run_tests_aurora.sh new file mode 100755 index 000000000..f08fb4783 --- /dev/null +++ b/scripts/run_tests_aurora.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +function set_bash_error_handling() { + set -euo pipefail +} + +function go_to_project_root_directory() { + local -r script_dir=$( dirname "${BASH_SOURCE[0]}") + + cd "$script_dir/.." +} + +function clean_test_databases_mysql() { + echo "🛁 Cleaning mysql databases" + echo "" + + local -r credhub_test_databases=$(mysql --user=root --protocol=tcp --execute="SHOW DATABASES LIKE 'credhub_test%';" -sN) + + for credhub_test_database in $credhub_test_databases; do + echo "Removing test database: $credhub_test_database" + # Use environment variables for remote connection, fallback to local defaults + # AURORA_DB_HOST: hostname of the Aurora/MySQL server (omit for localhost via TCP) + # AURORA_DB_PORT: port number (omit for default 3306) + # AURORA_DB_USERNAME: database username (defaults to 'root') + # AURORA_DB_PASSWORD: database password (omit if empty) + mysql --protocol=tcp \ + ${AURORA_DB_HOST:+--host=$AURORA_DB_HOST} \ + ${AURORA_DB_PORT:+--port=$AURORA_DB_PORT} \ + --user=${AURORA_DB_USERNAME:-root} \ + ${AURORA_DB_PASSWORD:+--password=$AURORA_DB_PASSWORD} \ + --execute "DROP DATABASE IF EXISTS $credhub_test_database;" + echo "" + done + + echo "🏗️ Creating mysql test database: credhub_test" + mysql --user=root --protocol=tcp --execute "CREATE DATABASE credhub_test;" + echo "" +} + +function run_tests_aurora() { + local gradle_test_command="test" + echo "✨ Parallel test mode enabled" + echo "🚀 Running aurora tests" + echo "" + + mysql --protocol=tcp --user=root --protocol=tcp --execute "SET GLOBAL max_connections = 1000;" + ./gradlew clean $gradle_test_command -Dspring.profiles.active=unit-test-aurora +} + +function main() { + set_bash_error_handling + go_to_project_root_directory + + clean_test_databases_mysql + run_tests_aurora +} + +main