goose/internal/testdb/turso.go

100 lines
2.6 KiB
Go

package testdb
import (
"database/sql"
"fmt"
"log"
"strconv"
_ "github.com/libsql/libsql-client-go/libsql"
"github.com/ory/dockertest/v3"
"github.com/ory/dockertest/v3/docker"
)
const (
// ghcr.io/tursodatabase/libsql-server:v0.22.10
TURSO_IMAGE = "ghcr.io/tursodatabase/libsql-server"
TURSO_VERSION = "v0.22.10"
TURSO_PORT = "8080"
)
// NewTurso starts a Turso docker container. Returns db connection and a docker cleanup function.
func NewTurso(options ...OptionsFunc) (db *sql.DB, cleanup func(), err error) {
return newTurso(options...)
}
func newTurso(opts ...OptionsFunc) (*sql.DB, func(), error) {
option := &options{}
for _, f := range opts {
f(option)
}
// Uses a sensible default on windows (tcp/http) and linux/osx (socket).
pool, err := dockertest.NewPool("")
if err != nil {
return nil, nil, err
}
runOptions := &dockertest.RunOptions{
Repository: TURSO_IMAGE,
Tag: TURSO_VERSION,
Labels: map[string]string{"goose_test": "1"},
PortBindings: make(map[docker.Port][]docker.PortBinding),
}
if option.debug {
runOptions.Env = append(runOptions.Env, "RUST=trace")
} else {
runOptions.Env = append(runOptions.Env, "RUST=error")
}
if option.bindPort > 0 {
runOptions.PortBindings[TURSO_PORT+"/tcp"] = []docker.PortBinding{
{HostPort: strconv.Itoa(option.bindPort)},
}
}
container, err := pool.RunWithOptions(
runOptions,
func(config *docker.HostConfig) {
// Set AutoRemove to true so that stopped container goes away by itself.
config.AutoRemove = true
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
},
)
if err != nil {
return nil, nil, err
}
cleanup := func() {
if option.debug {
// User must manually delete the Docker container.
return
}
if err := pool.Purge(container); err != nil {
log.Printf("failed to purge resource: %v", err)
}
}
// Fetch port assigned to container
var db *sql.DB
// Exponential backoff-retry, because the application in the container
// might not be ready to accept connections yet.
if err := pool.Retry(func() error {
db, err = tursoOpenDB(container)
return err
}); err != nil {
return nil, cleanup, fmt.Errorf("could not connect to docker database: %w", err)
}
return db, cleanup, nil
}
func tursoOpenDB(container *dockertest.Resource) (*sql.DB, error) {
address := fmt.Sprintf("http://127.0.0.1:%s", container.GetPort(TURSO_PORT+"/tcp"))
db, err := sql.Open("libsql", address)
if err != nil {
return db, err
}
// let's do a ping to be sure we are connected
var result int
err = db.QueryRow("SELECT 1").Scan(&result)
if err != nil {
return nil, err
}
return db, nil
}