这个项目能做什么

# go-docker-testsuite 一个用于在 Docker 容器中运行第三方依赖以进行集成测试的 Go 库。它让你可以启动任意 Docker 镜像——数据库、队列、缓存或对象存储——并从 Go 测试中连接它们。 ## 特性 - **Container** — 用于创建、运行、等待输出并清理任意 Docker 镜像的底层封装 - **Group** — 在具有 IP 级连通性的隔离 Docker 网络中运行多个容器 - **Applications** — 针对流行服务(MySQL、PostgreSQL、Redis、Kafka 等)的开箱即用封装 - **Hooks** — 每个容器的生命周期回调(BeforeRun、AfterRun、BeforeClose、AfterClose) - **Exec** — 在运行中的容器内执行命令并捕获 stdout、stderr 和退出码 - **生命周期命令** — 通过 exec 运行启动 / 就绪后命令 - **将文件复制到容器中** — 使用 `WithFiles` 在启动前预置文件 - **从容器中复制文件** — 以 tar 流的形式从运行中的容器读回文件 - **资源限制** — 限制 CPU/内存/pids 以保护主机和 CI - **匹配器** — 使用子串、精确或正则匹配器等待容器日志 - **等待策略** — 可组合的就绪探针(ForLog、ForHTTPGet、ForCommand、ForTCPConnection) - **环境构建器** — 用于类型化环境变量的流式 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 路由套件,配合 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 + 管理 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) // 快速失败;注册 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