Architecture
gtool is a single Go binary. The CLI (Cobra) builds an orchestrator that drives three phase managers; Docker access goes through one wrapper around the Docker SDK.
graph TB
CLI[CLI - Cobra] --> Orch[Orchestrator]
Orch --> Mock[Mock Manager]
Orch --> App[App Manager]
Orch --> Runner{Test phase}
Runner --> Karate[Karate Runner]
Runner --> Stress[Stress Runner]
Mock --> Plugins[Service Plugins]
Plugins --> Docker[Docker Client]
App --> Docker
Karate --> Docker
Orchestrator
internal/core/orchestrator runs mocks → app → test phase. The test phase is any
TestRunner:
type TestRunner interface {
Run(ctx context.Context, cfg *config.Config) (*plugin.TestResult, error)
}
gtool test plugs in the Karate runner (internal/core/test); gtool stress plugs
in the stress runner (internal/core/stress). The orchestrator records what it
started and, in a deferred cleanup with a fresh context, stops the app and then the
mocks — so teardown also happens on errors and cancellation.
Mock services
Each mock implements ServicePlugin (internal/plugin/interface.go) and is
registered in a thread-safe plugin.Registry. The mock manager starts the
configured plugins, waits for them to be ready and seeds them. Plugins read their
mock-config through internal/plugin/services/svcconfig. Adding a service
means implementing the interface, registering it in
internal/plugin/services/init.go and listing its default ports and accepted keys
in DefaultPorts() and ConfigKeys() (internal/plugin/services/defaults.go),
which drive the validator, auto ports and the GTOOL_* test environment.
Application under test
internal/core/app runs the app as a labelled container (DockerManager) and
polls its health-path. internal/core/app/nativeapp launches the
build-config.yml binaries as native processes for --native / --stable.
Stress runner
- Resolver — reads Swagger 2.0 and OpenAPI 3.x with libopenapi, applies filters, param values and overrides, and generates bodies from schemas.
- Engine — generates load in-process with vegeta, one constant-rate attack per stage over a shared keep-alive client, with weighted round-robin across targets.
- Detector — finds the degradation and breaking points from per-stage metrics.
- Report — JSON with a stable schema, a self-contained HTML page and a terminal summary.
Stable mode
gtool test --stable composes its own packages instead of the orchestrator:
internal/core/mock/stablemocks (fixed-contract mocks), internal/core/app/nativeapp
and internal/core/test/stablekarate (the fixed-contract Karate runner).
Cross-cutting
- Typed errors (
pkg/errors): every error carries a code such asERR_CONFIG_INVALIDorERR_TEST_FAILED. - Structured logging with Zap (
pkg/logger). - Strict configuration (
internal/core/config): loader with environment expansion and a strict validator. - Ports (
internal/core/ports): resolvesautoports and the${mocks.*}/${app.*}placeholders for each run. - Run IDs (
internal/infra/docker/runid.go): labels and names each run's containers so concurrent runs never touch each other. - Readiness (
internal/core/readiness): waits for the app'shealth-pathbefore tests run. - CLI config loading (
internal/cli/cliconfig): one policy for missing or invalid config files across commands. - Terminal output (
internal/infra/termout): symbols on an interactive terminal, plain text with a pipe,NO_COLORorCI.