Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MySQL module #943

Merged
merged 14 commits into from
Mar 23, 2023
89 changes: 41 additions & 48 deletions modules/mysql/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"path/filepath"
"strings"
)

Expand All @@ -17,61 +16,53 @@ const defaultDatabaseName = "test"
// MySQLContainer represents the MySQL container type used in the module
type MySQLContainer struct {
testcontainers.Container
config *Config
username string
password string
database string
}

// StartContainer creates an instance of the MySQL container type
func StartContainer(ctx context.Context, image string, opts ...Option) (*MySQLContainer, error) {
config := &Config{
username: defaultUser,
password: defaultPassword,
database: defaultDatabaseName,
}

for _, opt := range opts {
opt(config)
}

mysqlEnv := map[string]string{}
mysqlEnv["MYSQL_DATABASE"] = config.database
if !strings.EqualFold(rootUser, config.username) {
mysqlEnv["MYSQL_USER"] = config.username
}
if len(config.password) != 0 && config.password != "" {
mysqlEnv["MYSQL_PASSWORD"] = config.password
mysqlEnv["MYSQL_ROOT_PASSWORD"] = config.password
} else if strings.EqualFold(rootUser, config.username) {
mysqlEnv["MYSQL_ALLOW_EMPTY_PASSWORD"] = "yes"
} else {
return nil, fmt.Errorf("empty password can be used only with the root user")
}
type PostgresContainerOption func(req *testcontainers.ContainerRequest)
eddumelendez marked this conversation as resolved.
Show resolved Hide resolved

// StartContainer creates an instance of the MySQL container type
func StartContainer(ctx context.Context, image string, opts ...PostgresContainerOption) (*MySQLContainer, error) {
eddumelendez marked this conversation as resolved.
Show resolved Hide resolved
req := testcontainers.ContainerRequest{
Image: image,
ExposedPorts: []string{"3306/tcp", "33060/tcp"},
Env: mysqlEnv,
WaitingFor: wait.ForLog("port: 3306 MySQL Community Server"),
Env: map[string]string{
"MYSQL_USER": defaultUser,
"MYSQL_PASSWORD": defaultPassword,
"MYSQL_DATABASE": defaultDatabaseName,
},
WaitingFor: wait.ForLog("port: 3306 MySQL Community Server"),
}

if config.configFile != "" {
cf := testcontainers.ContainerFile{
HostFilePath: config.configFile,
ContainerFilePath: "/etc/mysql/conf.d/my.cnf",
FileMode: 0755,
opts = append(opts, func(req *testcontainers.ContainerRequest) {
username := req.Env["MYSQL_USER"]
password := req.Env["MYSQL_PASSWORD"]
if strings.EqualFold(rootUser, username) {
delete(req.Env, "MYSQL_USER")
}
req.Files = append(req.Files, cf)
if len(password) != 0 && password != "" {
req.Env["MYSQL_ROOT_PASSWORD"] = password
} else if strings.EqualFold(rootUser, username) {
req.Env["MYSQL_ALLOW_EMPTY_PASSWORD"] = "yes"
delete(req.Env, "MYSQL_PASSWORD")
}
})
mdelapenya marked this conversation as resolved.
Show resolved Hide resolved

for _, opt := range opts {
opt(&req)
}

var initScripts []testcontainers.ContainerFile
for _, script := range config.scripts {
cf := testcontainers.ContainerFile{
HostFilePath: script,
ContainerFilePath: "/docker-entrypoint-initdb.d/" + filepath.Base(script),
FileMode: 0755,
}
initScripts = append(initScripts, cf)
username, ok := req.Env["MYSQL_USER"]
if !ok {
username = rootUser
}
password := req.Env["MYSQL_PASSWORD"]

if len(password) == 0 && password == "" && !strings.EqualFold(rootUser, username) {
return nil, fmt.Errorf("empty password can be used only with the root user")
mdelapenya marked this conversation as resolved.
Show resolved Hide resolved
}
req.Files = append(req.Files, initScripts...)

container, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Expand All @@ -81,17 +72,19 @@ func StartContainer(ctx context.Context, image string, opts ...Option) (*MySQLCo
return nil, err
}

return &MySQLContainer{container, config}, nil
database := req.Env["MYSQL_DATABASE"]

return &MySQLContainer{container, username, password, database}, nil
}

func (c *MySQLContainer) Username() string {
return c.config.username
return c.username
}

func (c *MySQLContainer) Password() string {
return c.config.password
return c.password
}

func (c *MySQLContainer) Database() string {
return c.config.database
return c.database
}
57 changes: 33 additions & 24 deletions modules/mysql/options.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,50 @@
package mysql

type Option func(*Config)
import (
"github.com/testcontainers/testcontainers-go"
"path/filepath"
)

type Config struct {
username string
password string
database string
configFile string
scripts []string
}

func WithUsername(username string) Option {
return func(config *Config) {
config.username = username
func WithUsername(username string) func(req *testcontainers.ContainerRequest) {
return func(req *testcontainers.ContainerRequest) {
req.Env["MYSQL_USER"] = username
}
}

func WithPassword(password string) Option {
return func(config *Config) {
config.password = password
func WithPassword(password string) func(req *testcontainers.ContainerRequest) {
return func(req *testcontainers.ContainerRequest) {
req.Env["MYSQL_PASSWORD"] = password
}
}

func WithDatabase(database string) Option {
return func(config *Config) {
config.database = database
func WithDatabase(database string) func(req *testcontainers.ContainerRequest) {
return func(req *testcontainers.ContainerRequest) {
req.Env["MYSQL_DATABASE"] = database
}
}

func WithConfigFile(configFile string) Option {
return func(config *Config) {
config.configFile = configFile
func WithConfigFile(configFile string) func(req *testcontainers.ContainerRequest) {
return func(req *testcontainers.ContainerRequest) {
cf := testcontainers.ContainerFile{
HostFilePath: configFile,
ContainerFilePath: "/etc/mysql/conf.d/my.cnf",
FileMode: 0755,
}
req.Files = append(req.Files, cf)
}
}

func WithScripts(scripts ...string) Option {
return func(config *Config) {
config.scripts = scripts
func WithScripts(scripts ...string) func(req *testcontainers.ContainerRequest) {
return func(req *testcontainers.ContainerRequest) {
var initScripts []testcontainers.ContainerFile
for _, script := range scripts {
cf := testcontainers.ContainerFile{
HostFilePath: script,
ContainerFilePath: "/docker-entrypoint-initdb.d/" + filepath.Base(script),
FileMode: 0755,
}
initScripts = append(initScripts, cf)
}
req.Files = append(req.Files, initScripts...)
}
}