MySQL¶
dev.rightsize.modules.MySQLContainer — a single-node MySQL container. Defaults to a
test/test/test user/password/database trio so jdbcUrl is usable with zero
configuration.
Defaults¶
| Default image | mysql:8.4 |
| Exposed port | 3306 |
| Env | MYSQL_USER=test, MYSQL_PASSWORD=test, MYSQL_DATABASE=test, MYSQL_ROOT_PASSWORD=test |
| Wait strategy | Wait.forLogMessage(".*mysqld: ready for connections.*port: 3306($\|[^0-9]).*", times = 1) |
Helpers¶
| Member | Returns |
|---|---|
jdbcUrl: String |
A jdbc:mysql:// URL for the running container's databaseName |
username / password / databaseName: String |
The configured credentials/database (default test/test/test) |
withUsername(username: String): MySQLContainer |
Overrides MYSQL_USER |
withPassword(password: String): MySQLContainer |
Overrides MYSQL_PASSWORD |
withDatabase(database: String): MySQLContainer |
Overrides MYSQL_DATABASE |
Call the withX overrides before start().
Example¶
package dev.rightsize.modules
import dev.rightsize.modules.MySQLContainer
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import java.sql.DriverManager
class MySQLContainerTest {
@Test
fun `creates a table and reads it back`() {
val mysql = MySQLContainer()
mysql.start()
try {
DriverManager.getConnection(mysql.jdbcUrl, mysql.username, mysql.password).use { conn ->
conn.createStatement().use { it.execute("CREATE TABLE t (x INT)") }
conn.createStatement().use { it.execute("INSERT INTO t (x) VALUES (1)") }
conn.createStatement().use { st ->
st.executeQuery("SELECT x FROM t").use { rs ->
assertTrue(rs.next())
assertEquals(1, rs.getInt("x"))
}
}
}
} finally {
mysql.stop()
}
}
}
Backend notes: readiness, pinned empirically, not guessed¶
MySQL's entrypoint is a genuine trap for a naive log-message wait. It boots mysqld
twice — once as a throwaway "temp server" to run init scripts, then for real — and
four separate log lines contain the substring ready for connections: the temp
server's own line, the temp server's X Plugin line, the real server's X Plugin line,
and the real server's own line. Naively counting occurrences of ready for connections
is fragile, because the temp server's X Plugin line prints port: 33060 — whose
digits start with 3306 — so even an unanchored port: 3306 regex false-matches it.
Captured verbatim from a real docker run mysql:8.4 boot with this module's env:
[System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock
[System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.10' socket: '/var/run/mysqld/mysqld.sock' port: 0 MySQL Community Server - GPL.
...(init scripts run, temp server shuts down)...
[System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
[System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.10' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
The temp server's real-mysqld line prints port: 0 (no port bound yet); the X Plugin
lines print 33060. A naive times = 2 count would fire one full boot early, on the
temp server's own X-Plugin-plus-mysqld pair. This module's wait strategy instead
anchors the regex on the real server's exact line — port: 3306 followed by a
non-digit-or-end boundary, so it structurally cannot match 33060 — which makes
times = 1 unambiguous: that exact line appears once, only after the real server is
listening.
If you're writing a GenericContainer wait strategy for a different MySQL-family
image, or extending this pattern elsewhere, capture a real boot log first rather than
assuming a single readiness line — see MariaDB for the sibling case that
follows the same precedent.