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

Canned mysql (2nd attempt) #107

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions canned/mysql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package canned

import (
"context"
"database/sql"
"fmt"

"github.com/testcontainers/testcontainers-go/wait"

_ "github.com/go-sql-driver/mysql"
"github.com/pkg/errors"
testcontainers "github.com/testcontainers/testcontainers-go"
)

const (
mysqlRootPassword = "root_password"
mysqlUser = "user"
mysqlPassword = "password"
mysqlDatabase = "database"
mysqlImage = "mysql"
mysqlDefaultTag = "8.0"
mysqlPort = "3306/tcp"
)

// MySQLContainerRequest represents some MySQL specific initialisation parameters
type MySQLContainerRequest struct {
testcontainers.GenericContainerRequest
RootPassword string
User string
Password string
Database string
}

// MySQLContainer should always be created via NewMySQLContainer
type MySQLContainer struct {
Container testcontainers.Container
db *sql.DB
req MySQLContainerRequest
}

// GetDriver returns an handle to the MySQL DB
func (c *MySQLContainer) GetDriver(ctx context.Context) (*sql.DB, error) {

host, err := c.Container.Host(ctx)
if err != nil {
return nil, err
}

mappedPort, err := c.Container.MappedPort(ctx, mysqlPort)
if err != nil {
return nil, err
}
db, err := sql.Open("mysql", fmt.Sprintf(
"%s:%s@tcp(%s:%d)/%s",
c.req.User,
c.req.Password,
host,
mappedPort.Int(),
c.req.Database,
))
if err != nil {
return nil, err
}

return db, nil
}

// NewMySQLContainer creates a MySQL in a container and optionally starts it
func NewMySQLContainer(ctx context.Context, req MySQLContainerRequest) (*MySQLContainer, error) {

provider, err := req.ProviderType.GetProvider()
if err != nil {
return nil, err
}

// With the current logic it's not really possible to allow other ports...
req.ExposedPorts = []string{mysqlPort}

if req.Env == nil {
req.Env = map[string]string{}
}

// Set the default values if none were provided in the request
if req.Image == "" && req.FromDockerfile.Context == "" {
req.Image = fmt.Sprintf("%s:%s", mysqlImage, mysqlDefaultTag)
}

if req.RootPassword == "" {
req.RootPassword = mysqlRootPassword
}

if req.User == "" {
req.User = mysqlUser
}

if req.Password == "" {
req.Password = mysqlPassword
}

if req.Database == "" {
req.Database = mysqlDatabase
}

req.Env["MYSQL_ROOT_PASSWORD"] = req.RootPassword
req.Env["MYSQL_USER"] = req.User
req.Env["MYSQL_PASSWORD"] = req.Password
req.Env["MYSQL_DATABASE"] = req.Database

req.WaitingFor = wait.ForLog("port: 3306 MySQL Community Server - GPL")

c, err := provider.CreateContainer(ctx, req.ContainerRequest)
if err != nil {
return nil, errors.Wrap(err, "failed to create container")
}

mysqlC := &MySQLContainer{
Container: c,
req: req,
}

if req.Started {
if err := c.Start(ctx); err != nil {
return mysqlC, errors.Wrap(err, "failed to start container")
}
}

return mysqlC, nil
}
85 changes: 85 additions & 0 deletions canned/mysql_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package canned

import (
"context"
"testing"

testcontainers "github.com/testcontainers/testcontainers-go"
)

func TestWriteIntoAMySQLContainerViaDriver(t *testing.T) {

ctx := context.Background()

c, err := NewMySQLContainer(ctx, MySQLContainerRequest{
RootPassword: "root",
Database: "hello",
GenericContainerRequest: testcontainers.GenericContainerRequest{
Started: true,
},
})
if err != nil {
t.Fatal(err.Error())
}
defer c.Container.Terminate(ctx)

sqlC, err := c.GetDriver(ctx)
if err != nil {
t.Fatal(err.Error())
}

_, err = sqlC.ExecContext(ctx, "CREATE TABLE example ( id integer, data varchar(32) )")
if err != nil {
t.Fatal(err.Error())
}
}

func ExampleMySQLContainerRequest() {

// Optional
containerRequest := testcontainers.ContainerRequest{
Image: "docker.io/library/mysql:8.0",
}

genericContainerRequest := testcontainers.GenericContainerRequest{
Started: true,
ContainerRequest: containerRequest,
}

// RootPassword, Database, User, and Password are optional,
// the driver will use default ones if not provided
mysqlContainerRequest := MySQLContainerRequest{
RootPassword: "rootpassword",
User: "anyuser",
Password: "yoursecurepassword",
Database: "mycustomdatabase",
GenericContainerRequest: genericContainerRequest,
}

mysqlContainerRequest.Validate()
}

func ExampleNewMySQLContainer() {
ctx := context.Background()

c, _ := NewMySQLContainer(ctx, MySQLContainerRequest{
GenericContainerRequest: testcontainers.GenericContainerRequest{
Started: true,
},
})
defer c.Container.Terminate(ctx)
}

func ExampleMySQLContainer_GetDriver() {
ctx := context.Background()

c, _ := NewMySQLContainer(ctx, MySQLContainerRequest{
GenericContainerRequest: testcontainers.GenericContainerRequest{
Started: true,
},
})

db, _ := c.GetDriver(ctx)

db.Ping()
}