Skip to content

Commit 8f4554f

Browse files
AnkBurovrnorth
authored andcommitted
Support init script at JdbcDatabaseContainer level (#575)
Currently init script can be applied only through ContainerDatabaseDriver which doesn't support private Docker registries. This limitation also denies generic container approach of usage. More information about the problem is in the corresponding issue: #532 So I've implemented an init script support for all Jdbc containers. Resolve #532
1 parent e2e8a2a commit 8f4554f

File tree

5 files changed

+105
-11
lines changed

5 files changed

+105
-11
lines changed

modules/database-commons/src/main/java/org/testcontainers/ext/ScriptUtils.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616

1717
package org.testcontainers.ext;
1818

19+
import org.apache.commons.io.IOUtils;
1920
import org.apache.commons.lang.StringUtils;
2021
import org.slf4j.Logger;
2122
import org.slf4j.LoggerFactory;
2223
import org.testcontainers.delegate.DatabaseDelegate;
2324

2425
import javax.script.ScriptException;
26+
import java.io.IOException;
27+
import java.net.URL;
28+
import java.nio.charset.StandardCharsets;
2529
import java.util.LinkedList;
2630
import java.util.List;
2731

@@ -210,6 +214,30 @@ public static boolean containsSqlScriptDelimiters(String script, String delim) {
210214
return false;
211215
}
212216

217+
/**
218+
* Load script from classpath and apply it to the given database
219+
*
220+
* @param databaseDelegate database delegate for script execution
221+
* @param initScriptPath the resource to load the init script from
222+
*/
223+
public static void runInitScript(DatabaseDelegate databaseDelegate, String initScriptPath) {
224+
try {
225+
URL resource = ScriptUtils.class.getClassLoader().getResource(initScriptPath);
226+
if (resource == null) {
227+
LOGGER.warn("Could not load classpath init script: {}", initScriptPath);
228+
throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath + ". Resource not found.");
229+
}
230+
String scripts = IOUtils.toString(resource, StandardCharsets.UTF_8);
231+
executeDatabaseScript(databaseDelegate, initScriptPath, scripts);
232+
} catch (IOException e) {
233+
LOGGER.warn("Could not load classpath init script: {}", initScriptPath);
234+
throw new ScriptLoadException("Could not load classpath init script: " + initScriptPath, e);
235+
} catch (ScriptException e) {
236+
LOGGER.error("Error while executing init script: {}", initScriptPath, e);
237+
throw new UncategorizedScriptException("Error while executing init script: " + initScriptPath, e);
238+
}
239+
}
240+
213241
public static void executeDatabaseScript(DatabaseDelegate databaseDelegate, String scriptPath, String script) throws ScriptException {
214242
executeDatabaseScript(databaseDelegate, scriptPath, script, false, false, DEFAULT_COMMENT_PREFIX, DEFAULT_STATEMENT_SEPARATOR, DEFAULT_BLOCK_COMMENT_START_DELIMITER, DEFAULT_BLOCK_COMMENT_END_DELIMITER);
215243
}

modules/jdbc-test/src/test/java/org/testcontainers/junit/SimpleMySQLTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,20 @@ public void testMySQL8() throws SQLException {
132132
}
133133
}
134134

135+
@Test
136+
public void testExplicitInitScript() throws SQLException {
137+
try (MySQLContainer container = (MySQLContainer) new MySQLContainer()
138+
.withInitScript("somepath/init_mysql.sql")
139+
.withLogConsumer(new Slf4jLogConsumer(logger))) {
140+
container.start();
141+
142+
ResultSet resultSet = performQuery(container, "SELECT foo FROM bar");
143+
String firstColumnValue = resultSet.getString(1);
144+
145+
assertEquals("Value from init script should equal real value", "hello world", firstColumnValue);
146+
}
147+
}
148+
135149
@Test
136150
public void testEmptyPasswordWithNonRootUser() {
137151

modules/jdbc-test/src/test/java/org/testcontainers/junit/SimplePostgreSQLTest.java

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import com.zaxxer.hikari.HikariConfig;
44
import com.zaxxer.hikari.HikariDataSource;
5-
import org.junit.Rule;
65
import org.junit.Test;
6+
import org.testcontainers.containers.JdbcDatabaseContainer;
77
import org.testcontainers.containers.PostgreSQLContainer;
88

99
import java.sql.ResultSet;
@@ -17,23 +17,42 @@
1717
*/
1818
public class SimplePostgreSQLTest {
1919

20-
@Rule
21-
public PostgreSQLContainer postgres = new PostgreSQLContainer();
22-
2320
@Test
2421
public void testSimple() throws SQLException {
22+
try (PostgreSQLContainer postgres = new PostgreSQLContainer<>()) {
23+
postgres.start();
24+
25+
ResultSet resultSet = performQuery(postgres, "SELECT 1");
26+
27+
int resultSetInt = resultSet.getInt(1);
28+
assertEquals("A basic SELECT query succeeds", 1, resultSetInt);
29+
}
30+
}
31+
32+
@Test
33+
public void testExplicitInitScript() throws SQLException {
34+
try (PostgreSQLContainer postgres = new PostgreSQLContainer<>()
35+
.withInitScript("somepath/init_postgresql.sql")) {
36+
postgres.start();
37+
38+
ResultSet resultSet = performQuery(postgres, "SELECT foo FROM bar");
39+
40+
String firstColumnValue = resultSet.getString(1);
41+
assertEquals("Value from init script should equal real value", "hello world", firstColumnValue);
42+
}
43+
}
44+
45+
private ResultSet performQuery(JdbcDatabaseContainer container, String sql) throws SQLException {
2546
HikariConfig hikariConfig = new HikariConfig();
26-
hikariConfig.setJdbcUrl(postgres.getJdbcUrl());
27-
hikariConfig.setUsername(postgres.getUsername());
28-
hikariConfig.setPassword(postgres.getPassword());
47+
hikariConfig.setJdbcUrl(container.getJdbcUrl());
48+
hikariConfig.setUsername(container.getUsername());
49+
hikariConfig.setPassword(container.getPassword());
2950

3051
HikariDataSource ds = new HikariDataSource(hikariConfig);
3152
Statement statement = ds.getConnection().createStatement();
32-
statement.execute("SELECT 1");
53+
statement.execute(sql);
3354
ResultSet resultSet = statement.getResultSet();
34-
3555
resultSet.next();
36-
int resultSetInt = resultSet.getInt(1);
37-
assertEquals("A basic SELECT query succeeds", 1, resultSetInt);
56+
return resultSet;
3857
}
3958
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATE TABLE bar (
2+
foo VARCHAR(255)
3+
);
4+
5+
INSERT INTO bar (foo) VALUES ('hello world');

modules/jdbc/src/main/java/org/testcontainers/containers/JdbcDatabaseContainer.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package org.testcontainers.containers;
22

33
import lombok.NonNull;
4+
import com.github.dockerjava.api.command.InspectContainerResponse;
45
import org.jetbrains.annotations.NotNull;
56
import org.rnorth.ducttape.ratelimits.RateLimiter;
67
import org.rnorth.ducttape.ratelimits.RateLimiterBuilder;
78
import org.rnorth.ducttape.unreliables.Unreliables;
89
import org.testcontainers.containers.traits.LinkableContainer;
10+
import org.testcontainers.delegate.DatabaseDelegate;
11+
import org.testcontainers.ext.ScriptUtils;
12+
import org.testcontainers.jdbc.JdbcDatabaseDelegate;
913
import org.testcontainers.utility.MountableFile;
1014

1115
import java.sql.Connection;
@@ -26,6 +30,7 @@ public abstract class JdbcDatabaseContainer<SELF extends JdbcDatabaseContainer<S
2630

2731
private static final Object DRIVER_LOAD_MUTEX = new Object();
2832
private Driver driver;
33+
private String initScriptPath;
2934
protected Map<String, String> parameters = new HashMap<>();
3035

3136
private static final RateLimiter DB_CONNECT_RATE_LIMIT = RateLimiterBuilder.newBuilder()
@@ -111,6 +116,11 @@ public SELF withConnectTimeoutSeconds(int connectTimeoutSeconds) {
111116
return self();
112117
}
113118

119+
public SELF withInitScript(String initScriptPath) {
120+
this.initScriptPath = initScriptPath;
121+
return self();
122+
}
123+
114124
@Override
115125
protected void waitUntilContainerStarted() {
116126
// Repeatedly try and open a connection to the DB and execute a test query
@@ -135,6 +145,11 @@ protected void waitUntilContainerStarted() {
135145
});
136146
}
137147

148+
@Override
149+
protected void containerIsStarted(InspectContainerResponse containerInfo) {
150+
runInitScriptIfRequired();
151+
}
152+
138153
/**
139154
* Obtain an instance of the correct JDBC driver for this particular database container type
140155
*
@@ -202,6 +217,15 @@ protected void optionallyMapResourceParameterAsVolume(@NotNull String paramName,
202217
}
203218
}
204219

220+
/**
221+
* Load init script content and apply it to the database if initScriptPath is set
222+
*/
223+
protected void runInitScriptIfRequired() {
224+
if (initScriptPath != null) {
225+
ScriptUtils.runInitScript(getDatabaseDelegate(), initScriptPath);
226+
}
227+
}
228+
205229
public void setParameters(Map<String, String> parameters) {
206230
this.parameters = parameters;
207231
}
@@ -228,4 +252,8 @@ protected int getStartupTimeoutSeconds() {
228252
protected int getConnectTimeoutSeconds() {
229253
return connectTimeoutSeconds;
230254
}
255+
256+
protected DatabaseDelegate getDatabaseDelegate() {
257+
return new JdbcDatabaseDelegate(this, "");
258+
}
231259
}

0 commit comments

Comments
 (0)