Об этом проекте

# go-docker-testsuite Библиотека Go для запуска сторонних зависимостей в контейнерах Docker при интеграционном тестировании. Она позволяет поднять любой образ Docker — базы данных, очереди, кэши или объектное хранилище — и подключиться к ним из ваших тестов на Go. ## Возможности - **Container** — низкоуровневая обёртка для создания, запуска, ожидания вывода и очистки любого образа Docker - **Group** — запуск нескольких контейнеров в изолированной сети Docker с IP-связностью - **Applications** — готовые обёртки для популярных сервисов (MySQL, PostgreSQL, Redis, Kafka и др.) - **Hooks** — колбэки жизненного цикла (BeforeRun, AfterRun, BeforeClose, AfterClose) для каждого контейнера - **Exec** — выполнение команд внутри запущенного контейнера с захватом stdout, stderr и кода выхода - **Команды жизненного цикла** — команды запуска / после готовности через exec - **Копирование файлов в контейнеры** — предварительная загрузка файлов перед стартом с помощью `WithFiles` - **Копирование файлов из контейнеров** — чтение файлов из запущенного контейнера в виде tar-потока - **Ограничения ресурсов** — ограничение CPU/памяти/pids для защиты хоста и CI - **Matchers** — ожидание логов контейнера с сопоставлением по подстроке, точному совпадению или regexp - **Стратегии ожидания** — композируемые проверки готовности (ForLog, ForHTTPGet, ForCommand, ForTCPConnection) - **Конструктор окружения** — fluent DSL для типизированных переменных окружения - **Привязки портов** — DNAT-маппинг портов со случайным или взаимно-однозначным распределением - **Сетевой режим** — поддержка host или пользовательской сети - **IMAGE_PREFIX** — маршрутизация образов через прокси/зеркало - **Привязка `*testing.T`** — автоматическая очистка и логирование с безопасным `t.Parallel()` ## Требования - Go 1.26+ - Запущенный демон Docker (работает с удалёнными хостами через `DOCKER_HOST`) ## Установка Многомодульное рабочее пространство: корень — это основной модуль, а каждое приложение в `applications/<name>` — отдельный модуль Go. Подключайте только необходимое: ```sh go get github.com/teran/go-docker-testsuite # Конкретная обёртка приложения: go get github.com/teran/go-docker-testsuite/applications/redis go get github.com/teran/go-docker-testsuite/applications/postgres ``` ## Приложения Готовые обёртки (каждая возвращает типизированный интерфейс клиента и обрабатывает запуск, проверки работоспособности и очистку): - Ceph (RGW) с AWS SDK v2 - ClickHouse с clickhouse-go - Forgejo с SQLite - FRR routing suite с vtysh - K3s с client-go - Kafka с Sarama - Memcache с gomemcache - MinIO (S3-совместимый) - MongoDB с mongo-driver - MySQL / MariaDB / Percona Server - NetBox с PostgreSQL + Redis - Nginx - OpenSearch с opensearch-go - Paperless-ngx с PostgreSQL + Valkey - PostgreSQL с pgx - Prometheus с prometheus/client_golang - RabbitMQ (AMQP + Management API) - Redis с go-redis - ScyllaDB с gocql - Vault - Libvirtd (KVM/QEMU) Многие пакеты включают тестируемые примеры на pkg.go.dev. ## Использование ### Быстрый старт — 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) } } ``` ### Группа из нескольких контейнеров ```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) } ``` ### Стратегии ожидания ```go if err := wait.Wait(ctx, c, wait.ForHTTPGet(8080, wait.WithPath("/health"), wait.WithResponseStatuses(200), )); err != nil { panic(err) } ``` Другие стратегии: `ForLog`, `ForCommand`, `ForTCPConnection`. Комбинируйте с `ForAll` / `ForAny` / `ForAtLeast`. ### Привязка `*testing.T` ```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; регистрирует t.Cleanup } ``` ### Хуки жизненного цикла ```go docker.HookTypeBeforeRun // перед запуском контейнера docker.HookTypeAfterRun // после запуска контейнера docker.HookTypeBeforeClose // перед остановкой контейнера docker.HookTypeAfterClose // после остановки контейнера ``` ### Exec и команды жизненного цикла ```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) ``` ### Копирование файлов в контейнеры и из них Используйте `WithFiles` с `FileFromBytes` для небольшого содержимого или `io.Reader` + `Size` для больших файлов. Читайте файлы обратно с помощью `docker.CopyFromContainer`. ### Префикс образа / прокси ```sh export IMAGE_PREFIX=registry-mirror.example.com ``` ## Модули и релизы Многомодульное рабочее пространство с единым номером версии, тегируемое отдельно (например, ядро `v1.6.0`, приложение `applications/redis/v1.6.0`). Порядок релиза: сначала ядро, затем приложения. Используйте Makefile для тегирования. ## Лицензия Apache License, Version 2.0