About this project

# go-docker-testsuite A Go library to run third-party dependencies in Docker containers for integration testing. It lets you spin up any Docker image—databases, queues, caches, or object storage—and connect to them from your Go tests. ## Features - **Container** — low-level wrapper to create, run, await output, and clean up any Docker image - **Group** — run multiple containers in an isolated Docker network with IP-level connectivity - **Applications** — ready-to-use wrappers for popular services (MySQL, PostgreSQL, Redis, Kafka, etc.) - **Hooks** — lifecycle callbacks (BeforeRun, AfterRun, BeforeClose, AfterClose) per container - **Exec** — run commands inside a running container and capture stdout, stderr, and exit code - **Lifecycle commands** — run startup / after-ready commands via exec - **Copy files into containers** — seed files before start with `WithFiles` - **Copy files out of containers** — read files back out of a running container as a tar stream - **Resource limits** — cap CPU/memory/pids to protect the host and CI - **Matchers** — await container logs with substring, exact, or regexp matchers - **Wait strategies** — composable readiness probes (ForLog, ForHTTPGet, ForCommand, ForTCPConnection) - **Environment builder** — fluent DSL for typed environment variables - **Port bindings** — DNAT port mapping with random or one-to-one allocation - **Network mode** — host or custom network support - **IMAGE_PREFIX** — route images through a proxy/mirror - **`*testing.T` binding** — automatic teardown and logging with safe `t.Parallel()` ## Requirements - Go 1.26+ - A running Docker daemon (works with remote hosts via `DOCKER_HOST`) ## Installation Multi-module workspace: the root is the core module, and each application under `applications/<name>` is its own Go module. Pull only what you need: ```sh go get github.com/teran/go-docker-testsuite # Specific application wrapper: go get github.com/teran/go-docker-testsuite/applications/redis go get github.com/teran/go-docker-testsuite/applications/postgres ``` ## Applications Ready-to-use wrappers (each returns a typed client interface and handles startup, health checks, and cleanup): - Ceph (RGW) with AWS SDK v2 - ClickHouse with clickhouse-go - Forgejo with SQLite - FRR routing suite with vtysh - K3s with client-go - Kafka with Sarama - Memcache with gomemcache - MinIO (S3-compatible) - MongoDB with mongo-driver - MySQL / MariaDB / Percona Server - NetBox with PostgreSQL + Redis - Nginx - OpenSearch with opensearch-go - Paperless-ngx with PostgreSQL + Valkey - PostgreSQL with pgx - Prometheus with prometheus/client_golang - RabbitMQ (AMQP + Management API) - Redis with go-redis - ScyllaDB with gocql - Vault - Libvirtd (KVM/QEMU) Many packages include testable examples on pkg.go.dev. ## Usage ### Quick start — MySQL ```go package main import ( "context" "database/sql" "time" _ "github.com/go-sql-driver/mysql" "github.com/teran/go-docker-testsuite/applications/mysql" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() app, err := mysql.New(ctx, "index.docker.io/library/mysql:8.0.4") if err != nil { panic(err) } defer app.Close(ctx) if err := app.CreateDB(ctx, "important_database"); err != nil { panic(err) } db, err := sql.Open("mysql", app.MustDSN("important_database")) if err != nil { panic(err) } defer db.Close() if _, err := db.ExecContext(ctx, "SELECT 1"); err != nil { panic(err) } } ``` ### Multi-container group ```go package main import ( "context" "time" "github.com/teran/go-docker-testsuite" "github.com/teran/go-docker-testsuite/wait" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() app := docker.NewApplication( c, docker.HookFunc(func(ctx context.Context, ht docker.HookType, c docker.Container) error { return wait.Wait(ctx, c, wait.ForLog(docker.NewSubstringMatcher("ready"))) }), ) g, err := docker.NewGroup("my-services", app1, app2) if err != nil { panic(err) } if err := g.Run(ctx); err != nil { panic(err) } defer g.Close(ctx) } ``` ### Wait strategies ```go if err := wait.Wait(ctx, c, wait.ForHTTPGet(8080, wait.WithPath("/health"), wait.WithResponseStatuses(200), )); err != nil { panic(err) } ``` Other strategies: `ForLog`, `ForCommand`, `ForTCPConnection`. Combine with `ForAll` / `ForAny` / `ForAtLeast`. ### `*testing.T` binding ```go func TestRedis(t *testing.T) { t.Parallel() c, err := docker.NewContainerWithT( t, "redis", images.Redis, nil, docker.NewEnvironment(), docker.NewPortBindings().PortDNAT(docker.ProtoTCP, 6379), ) if err != nil { t.Fatal(err) } c.RunT(ctx) // fail-fast; registers t.Cleanup } ``` ### Lifecycle hooks ```go docker.HookTypeBeforeRun // before container starts docker.HookTypeAfterRun // after container starts docker.HookTypeBeforeClose // before container stops docker.HookTypeAfterClose // after container stops ``` ### Exec and lifecycle commands ```go res, err := c.Exec(ctx, []string{"echo", "hello"}) if err != nil { panic(err) } if err := res.Error(); err != nil { panic(err) } fmt.Printf("exit code: %d\n", res.ExitCode) fmt.Printf("stdout: %s", res.Stdout) ``` ### Copying files into/out of containers Use `WithFiles` with `FileFromBytes` for small content or an `io.Reader` + `Size` for large files. Read files back with `docker.CopyFromContainer`. ### Image prefix / proxy ```sh export IMAGE_PREFIX=registry-mirror.example.com ``` ## Modules & releases Multi-module workspace with a single version number, tagged separately (e.g., core `v1.6.0`, application `applications/redis/v1.6.0`). Release order: core first, then applications. Use the Makefile to tag. ## License Apache License, Version 2.0