Skip to content

Commit c291de9

Browse files
committed
feat(hive): add MySQL support for Hive Metastore with configurable port
- Introduced `databaseType` option to switch between PostgreSQL (default) and MySQL for Hive Metastore. - Added `databaseHostPort` to enable stable local ports for MySQL and PostgreSQL support containers. - Updated Gradle plugin and test framework to integrate MySQL configuration. - Enhanced documentation with examples for MySQL usage and configuration options.
1 parent 63c5226 commit c291de9

16 files changed

Lines changed: 270 additions & 34 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ Host-side Kafka Kerberos clients need a stable advertised host port. Existing Ke
9696

9797
The project supports two Hive Metastore implementations:
9898

99-
- `hiveMetastore = true`: open-source Hive HMS image plus external PostgreSQL.
99+
- `hiveMetastore = true`: open-source Hive HMS image plus external PostgreSQL by default, or MySQL with `databaseType = "mysql"`.
100100
- `clouderaHms = true`: Cloudera HMS image with embedded PostgreSQL.
101101

102102
Keep their endpoint contract the same through `BigDataService.HIVE_METASTORE`. A test must enable only one HMS implementation.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Composable Testcontainers fixtures for local big-data integration tests.
66
connection properties through a small Kotlin/JUnit API. Heavier setup such as
77
S3 JCEKS generation, bucket creation, and Kafka Avro seeding lives in the
88
optional `extensions` module so `core` and `junit5` stay lightweight.
9+
Open-source Hive Metastore uses PostgreSQL by default and can be switched to
10+
MySQL with TOML `databaseType = "mysql"` under `[hiveMetastore]`. Its support
11+
database uses a random host port by default; set `databaseHostPort` when you
12+
need a stable local port.
913

1014
## Modules
1115

autoconfigure/src/main/kotlin/org/openprojectx/bigdata/test/autoconfigure/BigDataTestAutoConfiguration.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import org.openprojectx.bigdata.test.core.BigDataService
55
import org.openprojectx.bigdata.test.core.ContainerLogOptions
66
import org.openprojectx.bigdata.test.core.HdfsOptions
77
import org.openprojectx.bigdata.test.core.HttpTlsOptions
8+
import org.openprojectx.bigdata.test.core.HiveMetastoreDatabaseType
89
import org.openprojectx.bigdata.test.core.HiveMetastoreDistribution
910
import org.openprojectx.bigdata.test.core.HiveMetastoreOptions
1011
import org.openprojectx.bigdata.test.core.KafkaOptions
@@ -100,10 +101,12 @@ class BigDataTestAutoConfiguration {
100101
enabled = true,
101102
distribution = HiveMetastoreDistribution.OPEN_SOURCE,
102103
image = properties.hiveMetastore.image,
103-
databaseImage = properties.hiveMetastore.databaseImage,
104+
databaseType = properties.hiveMetastore.databaseType,
105+
databaseImage = properties.hiveMetastore.databaseImageForType(),
104106
databaseName = properties.hiveMetastore.databaseName,
105107
databaseUser = properties.hiveMetastore.databaseUser,
106108
databasePassword = properties.hiveMetastore.databasePassword,
109+
databaseHostPort = properties.hiveMetastore.databaseHostPort,
107110
warehouseDir = properties.hiveMetastore.warehouseDir,
108111
extraConfiguration = properties.hiveMetastore.extraConfiguration,
109112
kerberos = KerberosAuthOptions(
@@ -199,4 +202,15 @@ class BigDataTestAutoConfiguration {
199202
service.name.replace("_", "-").equals(this, ignoreCase = true) ||
200203
service.name.replace("_", "").equals(this, ignoreCase = true)
201204
} ?: error("Unknown bigdata.test.container-log-levels service '$this'")
205+
206+
private fun BigDataTestProperties.HiveMetastore.databaseImageForType(): String =
207+
when (databaseType) {
208+
HiveMetastoreDatabaseType.POSTGRESQL -> databaseImage
209+
HiveMetastoreDatabaseType.MYSQL ->
210+
if (databaseImage == HiveMetastoreOptions.DEFAULT_POSTGRES_IMAGE) {
211+
HiveMetastoreOptions.DEFAULT_MYSQL_IMAGE
212+
} else {
213+
databaseImage
214+
}
215+
}
202216
}

autoconfigure/src/main/kotlin/org/openprojectx/bigdata/test/autoconfigure/BigDataTestProperties.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package org.openprojectx.bigdata.test.autoconfigure
22

3+
import org.openprojectx.bigdata.test.core.HiveMetastoreDatabaseType
4+
import org.openprojectx.bigdata.test.core.HiveMetastoreOptions
5+
36
import org.openprojectx.bigdata.test.core.ContainerLogMode
47
import org.springframework.boot.context.properties.ConfigurationProperties
58

@@ -64,11 +67,13 @@ data class BigDataTestProperties(
6467

6568
data class HiveMetastore(
6669
var enabled: Boolean = false,
67-
var image: String = "ghcr.io/openprojectx/hive:3.1.3-hadoop-3.4.2-gcs-4.0.4-jdk17-0.1.4",
68-
var databaseImage: String = "postgres:16-alpine",
70+
var image: String = HiveMetastoreOptions.DEFAULT_IMAGE,
71+
var databaseType: HiveMetastoreDatabaseType = HiveMetastoreDatabaseType.POSTGRESQL,
72+
var databaseImage: String = HiveMetastoreOptions.DEFAULT_POSTGRES_IMAGE,
6973
var databaseName: String = "metastore",
7074
var databaseUser: String = "hive",
7175
var databasePassword: String = "hive",
76+
var databaseHostPort: Int = 0,
7277
var warehouseDir: String = "/user/hive/warehouse",
7378
var extraConfiguration: Map<String, String> = emptyMap(),
7479
var kerberosEnabled: Boolean = false,

core/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ dependencies {
88
implementation(libs.bouncycastleProvider)
99
api(libs.testcontainers)
1010
api(libs.testcontainersPostgresql)
11+
api(libs.testcontainersMysql)
1112
api(libs.testcontainersKafka)
1213
api(libs.hiveDockerTestcontainers)
14+
runtimeOnly(libs.mysqlConnector)
1315
testImplementation(libs.junitJupiterApi)
1416
testRuntimeOnly(libs.junitJupiterEngine)
1517
testRuntimeOnly(libs.junitPlatformLauncher)

core/src/main/kotlin/org/openprojectx/bigdata/test/core/BigDataTestKitOptions.kt

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,10 @@ data class HdfsOptions(
8282
data class HiveMetastoreOptions(
8383
val enabled: Boolean = false,
8484
val distribution: HiveMetastoreDistribution = HiveMetastoreDistribution.OPEN_SOURCE,
85-
val image: String = "ghcr.io/openprojectx/hive:3.1.3-hadoop-3.4.2-gcs-4.0.4-jdk17-0.1.4",
86-
val databaseImage: String = "postgres:16-alpine",
85+
val image: String = DEFAULT_IMAGE,
86+
val databaseType: HiveMetastoreDatabaseType = HiveMetastoreDatabaseType.POSTGRESQL,
87+
val databaseImage: String = DEFAULT_POSTGRES_IMAGE,
88+
val databaseHostPort: Int = 0,
8789
val databaseName: String = "metastore",
8890
val databaseUser: String = "hive",
8991
val databasePassword: String = "hive",
@@ -96,13 +98,24 @@ data class HiveMetastoreOptions(
9698
servicePrincipal = "hive/hive-metastore.example.com@EXAMPLE.COM",
9799
keytabPath = "/kerby/keytabs/hive-metastore.keytab",
98100
),
99-
)
101+
) {
102+
companion object {
103+
const val DEFAULT_IMAGE = "ghcr.io/openprojectx/hive:3.1.3-hadoop-3.4.2-gcs-4.0.4-jdk17-0.1.5"
104+
const val DEFAULT_POSTGRES_IMAGE = "postgres:16-alpine"
105+
const val DEFAULT_MYSQL_IMAGE = "mysql:8.0.44-bookworm"
106+
}
107+
}
100108

101109
enum class HiveMetastoreDistribution {
102110
OPEN_SOURCE,
103111
CLOUDERA,
104112
}
105113

114+
enum class HiveMetastoreDatabaseType {
115+
POSTGRESQL,
116+
MYSQL,
117+
}
118+
106119
data class KafkaOptions(
107120
val enabled: Boolean = false,
108121
val image: String = "apache/kafka:4.1.2",

core/src/main/kotlin/org/openprojectx/bigdata/test/core/config/BigDataTestConfig.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.openprojectx.bigdata.test.core.ContainerFileTransferOptions
1010
import org.openprojectx.bigdata.test.core.ContainerLogMode
1111
import org.openprojectx.bigdata.test.core.ContainerMountOptions
1212
import org.openprojectx.bigdata.test.core.ContainerPortOptions
13+
import org.openprojectx.bigdata.test.core.HiveMetastoreDatabaseType
1314

1415
data class BigDataTestConfig(
1516
val images: BigDataTestImageConfig = BigDataTestImageConfig(),
@@ -80,6 +81,8 @@ data class BigDataTestHdfsConfig(
8081
}
8182

8283
data class BigDataTestHiveMetastoreConfig(
84+
val databaseType: HiveMetastoreDatabaseType? = null,
85+
val databaseHostPort: Int? = null,
8386
val databaseName: String? = null,
8487
val databaseUser: String? = null,
8588
val databasePassword: String? = null,
@@ -89,6 +92,8 @@ data class BigDataTestHiveMetastoreConfig(
8992
) {
9093
fun merge(override: BigDataTestHiveMetastoreConfig): BigDataTestHiveMetastoreConfig =
9194
BigDataTestHiveMetastoreConfig(
95+
databaseType = override.databaseType ?: databaseType,
96+
databaseHostPort = override.databaseHostPort ?: databaseHostPort,
9297
databaseName = override.databaseName ?: databaseName,
9398
databaseUser = override.databaseUser ?: databaseUser,
9499
databasePassword = override.databasePassword ?: databasePassword,
@@ -186,6 +191,7 @@ data class BigDataTestImageConfig(
186191
val hiveMetastore: String? = null,
187192
val clouderaHms: String? = null,
188193
val hiveMetastorePostgres: String? = null,
194+
val hiveMetastoreMysql: String? = null,
189195
val kafka: String? = null,
190196
val schemaRegistry: String? = null,
191197
val kafkaUi: String? = null,
@@ -199,6 +205,7 @@ data class BigDataTestImageConfig(
199205
hiveMetastore = override.hiveMetastore ?: hiveMetastore,
200206
clouderaHms = override.clouderaHms ?: clouderaHms,
201207
hiveMetastorePostgres = override.hiveMetastorePostgres ?: hiveMetastorePostgres,
208+
hiveMetastoreMysql = override.hiveMetastoreMysql ?: hiveMetastoreMysql,
202209
kafka = override.kafka ?: kafka,
203210
schemaRegistry = override.schemaRegistry ?: schemaRegistry,
204211
kafkaUi = override.kafkaUi ?: kafkaUi,
@@ -326,6 +333,7 @@ class BigDataTestConfigLoader(
326333
hiveMetastore = images.string("hiveMetastore"),
327334
clouderaHms = images.string("clouderaHms"),
328335
hiveMetastorePostgres = images.string("hiveMetastorePostgres"),
336+
hiveMetastoreMysql = images.string("hiveMetastoreMysql"),
329337
kafka = images.string("kafka"),
330338
schemaRegistry = images.string("schemaRegistry"),
331339
kafkaUi = images.string("kafkaUi"),
@@ -374,6 +382,8 @@ class BigDataTestConfigLoader(
374382
localHdfsSitePath = hdfs.string("localHdfsSitePath"),
375383
),
376384
hiveMetastore = BigDataTestHiveMetastoreConfig(
385+
databaseType = hiveMetastore.string("databaseType")?.let(::parseHiveMetastoreDatabaseType),
386+
databaseHostPort = hiveMetastore.int("databaseHostPort"),
377387
databaseName = hiveMetastore.string("databaseName"),
378388
databaseUser = hiveMetastore.string("databaseUser"),
379389
databasePassword = hiveMetastore.string("databasePassword"),
@@ -516,6 +526,13 @@ class BigDataTestConfigLoader(
516526
domain = values.string("domain"),
517527
)
518528

529+
private fun parseHiveMetastoreDatabaseType(value: String): HiveMetastoreDatabaseType =
530+
when (value.trim().lowercase().replace("-", "").replace("_", "")) {
531+
"postgres", "postgresql" -> HiveMetastoreDatabaseType.POSTGRESQL
532+
"mysql" -> HiveMetastoreDatabaseType.MYSQL
533+
else -> error("Hive Metastore databaseType must be one of postgresql, postgres, or mysql, got '$value'")
534+
}
535+
519536
private fun parseContainerCustomizations(
520537
tables: Map<String, Map<String, TomlValue>>,
521538
): Map<BigDataService, ContainerCustomizationOptions> {

0 commit comments

Comments
 (0)