spring-boot-testcontainers
supports automatic initialization of a mysql testcontainer instance simply by using jdbc url like:
jdbc:tc:mariadb:10.6.4:///test
This creates a default database named "test".
Problem: if you have a JdbcTemplate
configuration that makes use of multiple database schemas, this gets you into trouble.
#notice there is no database schema at the url definition!
spring.datasource.url=localhost:8080/
spring.datasource.username=test
spring.datasource.password=test
The jdbcTemplate
created from this can execute both select * from schema1.table
, as well as select * from schema2.table
.
But with a fixed testcontainers url, I'm required to bind the tc instance to either jdbc:tc:mariadb::///schema1
or jdbc:tc:mariadb::///schema2
.
Resolution:
To fix this, it would be sufficient to grant the default testcontainers test
user permissions to create any database schema himself. This could be done by the following url property:
jdbc:tc:mariadb:10.6.4:///test?TC_INITSCRIPT=create-testuser.sql
.
create-testuser.sql
:
-- CREATE USER 'test'@'%' IDENTIFIED BY 'test';
GRANT ALL PRIVILEGES ON *.* TO 'test'@'%';
FLUSH PRIVILEGES;
Problem here is that the TC_INITSCRIPT
is not executed with the root
user, which would have the permission to do so. Thus, the above fails with:
Caused by: org.testcontainers.ext.ScriptUtils$ScriptStatementFailedException: Script execution failed (create-testuser.sql:1): GRANT ALL PRIVILEGES ON *.* TO 'test'@'%'
at org.testcontainers.jdbc.JdbcDatabaseDelegate.execute(JdbcDatabaseDelegate.java:63)
at org.testcontainers.delegate.AbstractDatabaseDelegate.execute(AbstractDatabaseDelegate.java:38)
at org.testcontainers.ext.ScriptUtils.executeDatabaseScript(ScriptUtils.java:307)
... 19 more
Caused by: java.sql.SQLInvalidAuthorizationSpecException: (conn=5) Access denied for user 'test'@'%' (using password: YES)
at org.mariadb.jdbc.export.ExceptionFactory.createException(ExceptionFactory.java:293)
at org.mariadb.jdbc.export.ExceptionFactory.create(ExceptionFactory.java:378)
at org.mariadb.jdbc.message.ClientMessage.readPacket(ClientMessage.java:189)
at org.mariadb.jdbc.client.impl.StandardClient.readPacket(StandardClient.java:1235)
at org.mariadb.jdbc.client.impl.StandardClient.readResults(StandardClient.java:1174)
at org.mariadb.jdbc.client.impl.StandardClient.readResponse(StandardClient.java:1093)
at org.mariadb.jdbc.client.impl.StandardClient.execute(StandardClient.java:1017)
at org.mariadb.jdbc.Statement.executeInternal(Statement.java:1034)
So we either need a flag to tell testcontainers to run the TC_INITSCRIPT with the root
user. Or a new property to execute privileged actions explicit on container start.
By the way: a workaround would be to initialize the testcontainer manually, but that's rather bad as we have the jdbc-url feature, so it should support the below out of the box.
private static MariaDBContainer<?> CONTAINER;
static {
CONTAINER = new MariaDBContainer<>("mariadb:10.6.4")
.withUsername("root")
.withPassword("rootpw")
.withInitScript("create-testuser.sql");
CONTAINER.start();
}
@DynamicPropertySource
static void mysqlDatabaseInitializer(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.intranet.url", () -> CONTAINER.getJdbcUrl());
}
Comment From: wilkinsona
Thanks for the proposal but I don't think there's anything that's specific to Spring Boot here. As such, I think it would be better handled directly in Testcontainers. Looking at https://github.com/testcontainers/testcontainers-java/issues/3893, you may already be able to configure the user with the user
query parameter.