Skip to content

Commit

Permalink
HSEARCH-4674 Allow custom wait conditions for database containers
Browse files Browse the repository at this point in the history
  • Loading branch information
marko-bekhta committed Nov 8, 2023
1 parent 495e207 commit 0e645ee
Showing 1 changed file with 144 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@

import java.nio.file.Path;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;

import org.hibernate.cfg.JdbcSettings;
Expand All @@ -20,6 +22,9 @@
import com.github.dockerjava.api.model.Ulimit;

import org.testcontainers.containers.JdbcDatabaseContainer;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.images.builder.ImageFromDockerfile;

/*
Expand Down Expand Up @@ -51,20 +56,32 @@ private DatabaseContainer() {
}

public static Configuration configuration() {
if ( !SupportedDatabase.H2.equals( DATABASE ) ) {
DATABASE_CONTAINER.start();
// Let's see if an external DB connection was provided:
String url = System.getProperty( "jdbc.url" );
if ( url != null && !url.trim().isEmpty() ) {
// -Dhibernate.dialect=${db.dialect}
// -Dhibernate.connection.driver_class=${jdbc.driver}
// -Dhibernate.connection.url=${jdbc.url}
// -Dhibernate.connection.username=${jdbc.user}
// -Dhibernate.connection.password=${jdbc.pass}
// -Dhibernate.connection.isolation=${jdbc.isolation}
return DATABASE.configuration( url, DATABASE_CONTAINER )
.withDialect( System.getProperty( "db.dialect" ) )
.withDriver( System.getProperty( "jdbc.driver" ) )
.withUser( System.getProperty( "jdbc.user" ) )
.withPass( System.getProperty( "jdbc.pass" ) )
.withIsolation( System.getProperty( "jdbc.isolation" ) );
}
Configuration configuration = DATABASE.configuration( DATABASE_CONTAINER );

if ( DATABASE_CONTAINER != null && !DATABASE_CONTAINER.isRunning() ) {
synchronized (DATABASE_CONTAINER) {
if ( !DATABASE_CONTAINER.isRunning() ) {
DATABASE_CONTAINER.start();
else {
if ( DATABASE_CONTAINER != null && !DATABASE_CONTAINER.isRunning() ) {
synchronized (DATABASE_CONTAINER) {
if ( !DATABASE_CONTAINER.isRunning() ) {
DATABASE_CONTAINER.start();
}
}
}
return DATABASE.configuration( DATABASE_CONTAINER );
}

return configuration;
}

private enum SupportedDatabase {
Expand All @@ -86,6 +103,11 @@ Configuration configuration(JdbcDatabaseContainer<?> container) {
);
}

@Override
Configuration configuration(String jdbcUrl, JdbcDatabaseContainer<?> container) {
return configuration( container ).withUrl( jdbcUrl );
}

@Override
HibernateSearchJdbcDatabaseContainer container(Path dockerfile, String name) {
return null;
Expand All @@ -106,7 +128,10 @@ HibernateSearchJdbcDatabaseContainer container(Path dockerfile, String name) {
5432,
"hibernate_orm_test",
"hibernate_orm_test",
"select 1"
"select 1",
new LogMessageWaitStrategy()
.withRegEx( ".*database system is ready to accept connections.*\\s" )
.withTimes( 2 )
).withEnv( "POSTGRES_USER", "hibernate_orm_test" )
.withEnv( "POSTGRES_PASSWORD", "hibernate_orm_test" )
.withEnv( "POSTGRES_DB", "hibernate_orm_test" );
Expand Down Expand Up @@ -168,16 +193,19 @@ String dialect() {

@Override
HibernateSearchJdbcDatabaseContainer container(Path dockerfile, String name) {

return new HibernateSearchJdbcDatabaseContainer(
dockerfile, name,
"com.ibm.db2.jcc.DB2Driver",
"jdbc:db2://%s:%d/hreact",
"jdbc:db2://%s:%d/hreact:sslConnection=false;",
50000,
"hreact",
"hreact",
"SELECT 1 FROM SYSIBM.SYSDUMMY1"

).withNetworkMode( "bridge" )
"SELECT 1 FROM SYSIBM.SYSDUMMY1",
new LogMessageWaitStrategy()
.withRegEx( ".*Setup has completed\\..*" )
).withPrivilegedMode( true )
.withNetworkMode( "bridge" )
.withEnv( "DB2INSTANCE", "hreact" )
.withEnv( "DB2INST1_PASSWORD", "hreact" )
.withEnv( "DBNAME", "hreact" )
Expand Down Expand Up @@ -207,9 +235,11 @@ HibernateSearchJdbcDatabaseContainer container(Path dockerfile, String name) {
1521,
"SYSTEM",
"hibernate_orm_test",
"select 1 from dual"
).withEnv( "ORACLE_PASSWORD", "hibernate_orm_test" )
.withStartupTimeout( EXTENDED_TIMEOUT );
"select 1 from dual",
new LogMessageWaitStrategy()
.withRegEx( ".*DATABASE IS READY TO USE!.*\\s" )
.withTimes( 1 )
).withEnv( "ORACLE_PASSWORD", "hibernate_orm_test" );
}
},
MSSQL {
Expand All @@ -223,13 +253,13 @@ HibernateSearchJdbcDatabaseContainer container(Path dockerfile, String name) {
return new HibernateSearchJdbcDatabaseContainer(
dockerfile, name,
"com.microsoft.sqlserver.jdbc.SQLServerDriver",
"jdbc:sqlserver://%s:%d;databaseName=tempdb",
"jdbc:sqlserver://%s:%d;databaseName=tempdb;encrypt=true;trustServerCertificate=true;",
1433,
"SA",
"ActuallyRequired11Complexity",
"select 1"
).withEnv( "ACCEPT_EULA", "Y" )
.withEnv( "SA_PASSWORD", "ActuallyRequired11Complexity" )
.withEnv( "MSSQL_SA_PASSWORD", "ActuallyRequired11Complexity" )
.withStartupTimeout( EXTENDED_TIMEOUT );
}
},
Expand All @@ -248,7 +278,12 @@ HibernateSearchJdbcDatabaseContainer container(Path dockerfile, String name) {
26257,
"root",
"",
"select 1"
"select 1",
new HttpWaitStrategy()
.forPath( "/health" )
.forPort( 8080 )
.forStatusCode( 200 ),
8080
).withCommand( "start-single-node --insecure" )
.withStartupTimeout( EXTENDED_TIMEOUT )
.withCreateContainerCmdModifier( cmd -> {
Expand All @@ -272,6 +307,17 @@ Configuration configuration(JdbcDatabaseContainer<?> container) {
);
}

Configuration configuration(String jdbcUrl, JdbcDatabaseContainer<?> container) {
return new Configuration(
dialect(),
container.getDriverClassName(),
jdbcUrl,
container.getUsername(),
container.getPassword(),
""
);
}

abstract String dialect();

abstract HibernateSearchJdbcDatabaseContainer container(Path dockerfile, String name);
Expand All @@ -295,9 +341,16 @@ private static class HibernateSearchJdbcDatabaseContainer
private final String username;
private final String password;
private final String testQueryString;
private final Optional<WaitStrategy> customWaitStrategy;

public HibernateSearchJdbcDatabaseContainer(Path dockerfile, String name, String driverClassName, String jdbcUrlPattern,
int port, String username, String password, String testQueryString) {
this( dockerfile, name, driverClassName, jdbcUrlPattern, port, username, password, testQueryString, null );
}

public HibernateSearchJdbcDatabaseContainer(Path dockerfile, String name, String driverClassName, String jdbcUrlPattern,
int port, String username, String password, String testQueryString, WaitStrategy waitStrategy,
Integer... additionalExposedPorts) {
// IMPORTANT: we do not want to delete the image on exit as then we cannot use container reuse.
// (these two options are somewhat mutually exclusive).
super( new ImageFromDockerfile( "hibernate-search-" + name, false ).withDockerfile( dockerfile ) );
Expand All @@ -307,12 +360,38 @@ public HibernateSearchJdbcDatabaseContainer(Path dockerfile, String name, String
this.username = username;
this.password = password;
this.testQueryString = testQueryString;
this.customWaitStrategy = Optional.ofNullable( waitStrategy );

withExposedPorts( port );
if ( additionalExposedPorts.length == 0 ) {
withExposedPorts( port );
}
else {
Integer[] ports = Arrays.copyOf( additionalExposedPorts, additionalExposedPorts.length + 1 );
ports[additionalExposedPorts.length] = port;
withExposedPorts( ports );
}
withReuse( true );
withStartupTimeout( REGULAR_TIMEOUT );
}

@Override
protected void waitUntilContainerStarted() {
if ( customWaitStrategy.isPresent() ) {
customWaitStrategy.get().waitUntilReady( this );
}
else {
super.waitUntilContainerStarted();
}
}

@Override
public HibernateSearchJdbcDatabaseContainer withStartupTimeout(Duration startupTimeout) {
if ( customWaitStrategy.isPresent() ) {
customWaitStrategy.get().withStartupTimeout( startupTimeout );
}
return super.withStartupTimeout( startupTimeout );
}

@Override
public String getDriverClassName() {
return driverClassName;
Expand Down Expand Up @@ -348,7 +427,7 @@ public static class Configuration {
private final String pass;
private final String isolation;

public Configuration(String dialect, String driver, String url, String user, String pass, String isolation) {
private Configuration(String dialect, String driver, String url, String user, String pass, String isolation) {
this.dialect = dialect;
this.driver = driver;
this.url = url;
Expand Down Expand Up @@ -397,5 +476,47 @@ public void addAsSpring(BiConsumer<String, String> consumer) {
consumer.accept( "spring.datasource.username", this.user );
consumer.accept( "spring.datasource.password", this.pass );
}

private Configuration withDialect(String dialect) {
if ( dialect == null ) {
return this;
}
return new Configuration( dialect, driver, url, user, pass, isolation );
}

private Configuration withDriver(String driver) {
if ( driver == null ) {
return this;
}
return new Configuration( dialect, driver, url, user, pass, isolation );
}

private Configuration withUrl(String url) {
if ( url == null ) {
return this;
}
return new Configuration( dialect, driver, url, user, pass, isolation );
}

private Configuration withUser(String user) {
if ( user == null ) {
return this;
}
return new Configuration( dialect, driver, url, user, pass, isolation );
}

private Configuration withPass(String pass) {
if ( pass == null ) {
return this;
}
return new Configuration( dialect, driver, url, user, pass, isolation );
}

private Configuration withIsolation(String isolation) {
if ( isolation == null ) {
return this;
}
return new Configuration( dialect, driver, url, user, pass, isolation );
}
}
}

0 comments on commit 0e645ee

Please sign in to comment.