diff --git a/docs/modules/databases/mysql.md b/docs/modules/databases/mysql.md index 825ab42f60c..46bf8b586de 100644 --- a/docs/modules/databases/mysql.md +++ b/docs/modules/databases/mysql.md @@ -7,7 +7,7 @@ Testcontainers module for [MySQL](https://hub.docker.com/_/mysql) You can start a MySQL container instance from any Java application by using: -[Container definition](../../../modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java) inside_block:container +[Container definition](../../../modules/mysql/src/test/java/org/testcontainers/mysql/MySQLContainerTest.java) inside_block:container See [Database containers](./index.md) for documentation and usage that is common to all relational database container types. diff --git a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java index 54f3952b6a2..9f5b31531c4 100644 --- a/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java +++ b/modules/mysql/src/main/java/org/testcontainers/containers/MySQLContainer.java @@ -12,7 +12,10 @@ * Supported image: {@code mysql} *

* Exposed ports: 3306 + * + * @deprecated use {@link org.testcontainers.mysql.MySQLContainer} instead. */ +@Deprecated public class MySQLContainer> extends JdbcDatabaseContainer { public static final String NAME = "mysql"; diff --git a/modules/mysql/src/main/java/org/testcontainers/mysql/MySQLContainer.java b/modules/mysql/src/main/java/org/testcontainers/mysql/MySQLContainer.java new file mode 100644 index 00000000000..e46bef6180d --- /dev/null +++ b/modules/mysql/src/main/java/org/testcontainers/mysql/MySQLContainer.java @@ -0,0 +1,160 @@ +package org.testcontainers.mysql; + +import org.jetbrains.annotations.NotNull; +import org.testcontainers.containers.ContainerLaunchException; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.images.builder.Transferable; +import org.testcontainers.utility.DockerImageName; + +import java.util.Set; + +/** + * Testcontainers implementation for MySQL. + *

+ * Supported image: {@code mysql} + *

+ * Exposed ports: 3306 + */ +public class MySQLContainer extends JdbcDatabaseContainer { + + public static final String NAME = "mysql"; + + private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse("mysql"); + + static final String DEFAULT_USER = "test"; + + static final String DEFAULT_PASSWORD = "test"; + + private static final String MY_CNF_CONFIG_OVERRIDE_PARAM_NAME = "TC_MY_CNF"; + + public static final Integer MYSQL_PORT = 3306; + + private String databaseName = "test"; + + private String username = DEFAULT_USER; + + private String password = DEFAULT_PASSWORD; + + private static final String MYSQL_ROOT_USER = "root"; + + public MySQLContainer(String dockerImageName) { + this(DockerImageName.parse(dockerImageName)); + } + + public MySQLContainer(final DockerImageName dockerImageName) { + super(dockerImageName); + dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME); + + addExposedPort(MYSQL_PORT); + } + + /** + * @return the ports on which to check if the container is ready + * @deprecated use {@link #getLivenessCheckPortNumbers()} instead + */ + @NotNull + @Override + @Deprecated + protected Set getLivenessCheckPorts() { + return super.getLivenessCheckPorts(); + } + + @Override + protected void configure() { + optionallyMapResourceParameterAsVolume( + MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, + "/etc/mysql/conf.d", + "mysql-default-conf", + Transferable.DEFAULT_DIR_MODE + ); + + addEnv("MYSQL_DATABASE", databaseName); + if (!MYSQL_ROOT_USER.equalsIgnoreCase(username)) { + addEnv("MYSQL_USER", username); + } + if (password != null && !password.isEmpty()) { + addEnv("MYSQL_PASSWORD", password); + addEnv("MYSQL_ROOT_PASSWORD", password); + } else if (MYSQL_ROOT_USER.equalsIgnoreCase(username)) { + addEnv("MYSQL_ALLOW_EMPTY_PASSWORD", "yes"); + } else { + throw new ContainerLaunchException("Empty password can be used only with the root user"); + } + setStartupAttempts(3); + } + + @Override + public String getDriverClassName() { + try { + Class.forName("com.mysql.cj.jdbc.Driver"); + return "com.mysql.cj.jdbc.Driver"; + } catch (ClassNotFoundException e) { + return "com.mysql.jdbc.Driver"; + } + } + + @Override + public String getJdbcUrl() { + String additionalUrlParams = constructUrlParameters("?", "&"); + return "jdbc:mysql://" + getHost() + ":" + getMappedPort(MYSQL_PORT) + "/" + databaseName + additionalUrlParams; + } + + @Override + protected String constructUrlForConnection(String queryString) { + String url = super.constructUrlForConnection(queryString); + + if (!url.contains("useSSL=")) { + String separator = url.contains("?") ? "&" : "?"; + url = url + separator + "useSSL=false"; + } + + if (!url.contains("allowPublicKeyRetrieval=")) { + url = url + "&allowPublicKeyRetrieval=true"; + } + + return url; + } + + @Override + public String getDatabaseName() { + return databaseName; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getTestQueryString() { + return "SELECT 1"; + } + + public MySQLContainer withConfigurationOverride(String s) { + parameters.put(MY_CNF_CONFIG_OVERRIDE_PARAM_NAME, s); + return self(); + } + + @Override + public MySQLContainer withDatabaseName(final String databaseName) { + this.databaseName = databaseName; + return self(); + } + + @Override + public MySQLContainer withUsername(final String username) { + this.username = username; + return self(); + } + + @Override + public MySQLContainer withPassword(final String password) { + this.password = password; + return self(); + } +} diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java b/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java deleted file mode 100644 index 9e9d97dc54e..00000000000 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/CustomizableMysqlTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.testcontainers.junit.mysql; - -import org.junit.jupiter.api.Test; -import org.testcontainers.MySQLTestImages; -import org.testcontainers.containers.MySQLContainer; -import org.testcontainers.db.AbstractContainerDatabaseTest; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import static org.assertj.core.api.Assertions.assertThat; - -class CustomizableMysqlTest extends AbstractContainerDatabaseTest { - - private static final String DB_NAME = "foo"; - - private static final String USER = "bar"; - - private static final String PWD = "baz"; - - @Test - void testSimple() throws SQLException { - // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes - try ( - MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) - .withDatabaseName(DB_NAME) - .withUsername(USER) - .withPassword(PWD) - .withEnv("MYSQL_ROOT_HOST", "%") - ) { - mysql.start(); - - ResultSet resultSet = performQuery(mysql, "SELECT 1"); - - int resultSetInt = resultSet.getInt(1); - assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); - } - } -} diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/mysql/MultiVersionMySQLTest.java similarity index 87% rename from modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java rename to modules/mysql/src/test/java/org/testcontainers/mysql/MultiVersionMySQLTest.java index d8e8b52dc56..8dc4454860b 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/MultiVersionMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/mysql/MultiVersionMySQLTest.java @@ -1,9 +1,8 @@ -package org.testcontainers.junit.mysql; +package org.testcontainers.mysql; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.testcontainers.MySQLTestImages; -import org.testcontainers.containers.MySQLContainer; import org.testcontainers.db.AbstractContainerDatabaseTest; import org.testcontainers.utility.DockerImageName; @@ -26,7 +25,7 @@ public static DockerImageName[] params() { @ParameterizedTest @MethodSource("params") void versionCheckTest(DockerImageName dockerImageName) throws SQLException { - try (MySQLContainer mysql = new MySQLContainer<>(dockerImageName)) { + try (MySQLContainer mysql = new MySQLContainer(dockerImageName)) { mysql.start(); final ResultSet resultSet = performQuery(mysql, "SELECT VERSION()"); final String resultSetString = resultSet.getString(1); diff --git a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java b/modules/mysql/src/test/java/org/testcontainers/mysql/MySQLContainerTest.java similarity index 82% rename from modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java rename to modules/mysql/src/test/java/org/testcontainers/mysql/MySQLContainerTest.java index b3eac503c57..ad78781db5c 100644 --- a/modules/mysql/src/test/java/org/testcontainers/junit/mysql/SimpleMySQLTest.java +++ b/modules/mysql/src/test/java/org/testcontainers/mysql/MySQLContainerTest.java @@ -1,11 +1,10 @@ -package org.testcontainers.junit.mysql; +package org.testcontainers.mysql; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testcontainers.MySQLTestImages; import org.testcontainers.containers.ContainerLaunchException; -import org.testcontainers.containers.MySQLContainer; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.db.AbstractContainerDatabaseTest; @@ -30,14 +29,14 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assumptions.assumeThat; -class SimpleMySQLTest extends AbstractContainerDatabaseTest { +class MySQLContainerTest extends AbstractContainerDatabaseTest { - private static final Logger logger = LoggerFactory.getLogger(SimpleMySQLTest.class); + private static final Logger logger = LoggerFactory.getLogger(MySQLContainerTest.class); @Test void testSimple() throws SQLException { try ( // container { - MySQLContainer mysql = new MySQLContainer<>("mysql:8.0.36") + MySQLContainer mysql = new MySQLContainer("mysql:8.0.36") // } ) { mysql.start(); @@ -53,7 +52,7 @@ void testSimple() throws SQLException { @Test void testSpecificVersion() throws SQLException { try ( - MySQLContainer mysqlOldVersion = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer mysqlOldVersion = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override") .withLogConsumer(new Slf4jLogConsumer(logger)) ) { @@ -71,7 +70,7 @@ void testSpecificVersion() throws SQLException { @Test void testMySQLWithCustomIniFile() throws SQLException { try ( - MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer mysqlCustomConfig = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override") ) { mysqlCustomConfig.start(); @@ -83,7 +82,7 @@ void testMySQLWithCustomIniFile() throws SQLException { @Test void testCommandOverride() throws SQLException { try ( - MySQLContainer mysqlCustomConfig = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer mysqlCustomConfig = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withCommand("mysqld --auto_increment_increment=42") ) { mysqlCustomConfig.start(); @@ -98,7 +97,7 @@ void testCommandOverride() throws SQLException { @Test void testExplicitInitScript() throws SQLException { try ( - MySQLContainer container = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer container = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withInitScript("somepath/init_mysql.sql") .withLogConsumer(new Slf4jLogConsumer(logger)) ) { @@ -114,7 +113,7 @@ void testExplicitInitScript() throws SQLException { @Test void testEmptyPasswordWithNonRootUser() { try ( - MySQLContainer container = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer container = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withDatabaseName("TEST") .withUsername("test") .withPassword("") @@ -130,7 +129,7 @@ void testEmptyPasswordWithNonRootUser() { void testEmptyPasswordWithRootUser() throws SQLException { // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes try ( - MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer mysql = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withDatabaseName("foo") .withUsername("root") .withPassword("") @@ -147,7 +146,7 @@ void testEmptyPasswordWithRootUser() throws SQLException { @Test void testWithAdditionalUrlParamTimeZone() throws SQLException { - MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer mysql = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withUrlParam("serverTimezone", "Europe/Zurich") .withEnv("TZ", "Europe/Zurich") .withLogConsumer(new Slf4jLogConsumer(logger)); @@ -182,7 +181,7 @@ void testWithAdditionalUrlParamTimeZone() throws SQLException { @Test void testWithAdditionalUrlParamMultiQueries() throws SQLException { - MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer mysql = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withUrlParam("allowMultiQueries", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); mysql.start(); @@ -207,7 +206,7 @@ void testWithAdditionalUrlParamMultiQueries() throws SQLException { @Test void testWithAdditionalUrlParamInJdbcUrl() { - MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer mysql = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withUrlParam("allowMultiQueries", "true") .withUrlParam("rewriteBatchedStatements", "true") .withLogConsumer(new Slf4jLogConsumer(logger)); @@ -228,7 +227,7 @@ void testWithAdditionalUrlParamInJdbcUrl() { void testWithOnlyUserReadableCustomIniFile() throws Exception { assumeThat(FileSystems.getDefault().supportedFileAttributeViews().contains("posix")).isTrue(); try ( - MySQLContainer mysql = new MySQLContainer<>(MySQLTestImages.MYSQL_80_IMAGE) + MySQLContainer mysql = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) .withConfigurationOverride("somepath/mysql_conf_override") .withLogConsumer(new Slf4jLogConsumer(logger)) ) { @@ -252,12 +251,31 @@ void testWithOnlyUserReadableCustomIniFile() throws Exception { } } - private void assertHasCorrectExposedAndLivenessCheckPorts(MySQLContainer mysql) { + @Test + void testCustom() throws SQLException { + // Add MYSQL_ROOT_HOST environment so that we can root login from anywhere for testing purposes + try ( + MySQLContainer mysql = new MySQLContainer(MySQLTestImages.MYSQL_80_IMAGE) + .withDatabaseName("foo") + .withUsername("bar") + .withPassword("baz") + .withEnv("MYSQL_ROOT_HOST", "%") + ) { + mysql.start(); + + ResultSet resultSet = performQuery(mysql, "SELECT 1"); + + int resultSetInt = resultSet.getInt(1); + assertThat(resultSetInt).as("A basic SELECT query succeeds").isEqualTo(1); + } + } + + private void assertHasCorrectExposedAndLivenessCheckPorts(MySQLContainer mysql) { assertThat(mysql.getExposedPorts()).containsExactly(MySQLContainer.MYSQL_PORT); assertThat(mysql.getLivenessCheckPortNumbers()).containsExactly(mysql.getMappedPort(MySQLContainer.MYSQL_PORT)); } - private void assertThatCustomIniFileWasUsed(MySQLContainer mysql) throws SQLException { + private void assertThatCustomIniFileWasUsed(MySQLContainer mysql) throws SQLException { try (ResultSet resultSet = performQuery(mysql, "SELECT @@GLOBAL.innodb_max_undo_log_size")) { long result = resultSet.getLong(1); assertThat(result)