Documentation
¶
Overview ¶
Package wait provides composable readiness wait-strategies for Docker containers. It generalizes the library's log-line readiness model into reusable predicates (HTTP probes, in-container commands, raw TCP connects, log matching) combined with ForAll / ForAny / ForAtLeast.
A Strategy is a pure function predicate with strict (bool, error) semantics:
(true, nil) = ready now (false, nil) = not ready yet (retry) (_, err) = fatal — stop immediately
Wait owns the single shared poll loop; strategies never loop on their own. Timeouts and intervals are global to a Wait call, not per-strategy.
Index ¶
- func Wait(ctx context.Context, t Target, s Strategy, opts ...Option) error
- type Option
- func WithBodyContains(substr string) Option
- func WithExitCode(code int) Option
- func WithInterval(d time.Duration) Option
- func WithMethod(m string) Option
- func WithPath(p string) Option
- func WithResponseStatuses(codes ...int) Option
- func WithTLS(b bool) Option
- func WithTimeout(d time.Duration) Option
- type Strategy
- func ForAll(strategies ...Strategy) Strategy
- func ForAny(strategies ...Strategy) Strategy
- func ForAtLeast(x int, strategies ...Strategy) Strategy
- func ForCommand(cmd []string, opts ...Option) Strategy
- func ForHTTPGet(port uint16, opts ...Option) Strategy
- func ForLog(m docker.Matcher, opts ...Option) Strategy
- func ForTCPConnection(port uint16, opts ...Option) Strategy
- type Target
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Wait ¶
Wait runs a single strategy against t via the shared poller, honouring the ctx deadline. It returns nil as soon as the strategy reports ready, or a wrapped error on a fatal strategy error or on the deadline/timeout elapsing.
Example ¶
ExampleWait demonstrates the top-level Wait function: polling a target until a readiness strategy reports ready. Wait is the entry point for every strategy and combinator in this package.
err := Wait(context.Background(), &fakeTarget{}, ForCommand([]string{"echo", "ok"}))
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
fmt.Println("ready")
Output: ready
Types ¶
type Option ¶
type Option func(*config)
Option configures a Wait call or a strategy constructor. Options that do not apply to a particular consumer are ignored (see DESIGN §2.4).
func WithBodyContains ¶
WithBodyContains requires the response body to contain substr for the probe to be ready. Default "" (no body check).
func WithExitCode ¶
WithExitCode sets the expected exit code. Default 0.
func WithInterval ¶
WithInterval sets the poll interval. Non-positive values are clamped to the default (200ms). Default: 200ms.
func WithResponseStatuses ¶
WithResponseStatuses overrides the accepted status set (default: any 2xx).
func WithTLS ¶
WithTLS selects the https scheme. When true the per-request client trusts a self-signed certificate (InsecureSkipVerify) so the strategy can probe test TLS servers. Default false (http).
func WithTimeout ¶
WithTimeout bounds the whole Wait call when ctx carries no deadline. An explicit ctx deadline is authoritative and overrides this. Non-positive values are clamped to the default (60s). Default: 60s.
type Strategy ¶
Strategy is a readiness predicate. See the (bool, error) semantics table in the package documentation. It is a type alias (not a named type) so that one-line inline strategies are assignable while named strategies may still carry a String() method for diagnostics.
func ForAll ¶
ForAll returns a strategy that is ready when all sub-strategies are ready. An empty list is vacuously ready immediately.
Example ¶
ExampleForAll demonstrates combining strategies: ForAll reports ready only when every sub-strategy is ready. Here a command probe and a TCP liveness probe must both succeed.
err := Wait(
context.Background(),
&fakeTarget{},
ForAll(
ForCommand([]string{"echo", "ok"}),
ForCommand([]string{"true"}),
),
)
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
fmt.Println("ready")
Output: ready
func ForAny ¶
ForAny returns a strategy that is ready when at least one sub-strategy is ready. An empty list is never satisfiable and always times out.
Example ¶
ExampleForAny demonstrates combining strategies: ForAny reports ready as soon as at least one sub-strategy is ready.
err := Wait(
context.Background(),
&fakeTarget{},
ForAny(
ForCommand([]string{"false", "ignored"}),
ForCommand([]string{"echo", "ok"}),
),
)
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
fmt.Println("ready")
Output: ready
func ForAtLeast ¶
ForAtLeast returns a strategy that is ready when at least x of the sub-strategies are ready. Sub-strategies run concurrently for each poll via errgroup. Edge cases: x <= 0 is vacuous success; x > len(strategies) can never be satisfied (the poller times out).
Example ¶
ExampleForAtLeast demonstrates combining strategies: ForAtLeast(x, ...) is ready when at least x of the sub-strategies are ready.
err := Wait(
context.Background(),
&fakeTarget{},
ForAtLeast(1,
ForCommand([]string{"echo", "ok"}),
ForCommand([]string{"echo", "also ok"}),
),
)
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
fmt.Println("ready")
Output: ready
func ForCommand ¶
ForCommand returns a strategy that is ready when running cmd inside the container exits with the expected code (default 0 via WithExitCode).
The root package's Exec reports a non-zero exit code in the result, not as an error; only an infrastructure failure (create/attach/IO) is returned as an error and treated as fatal.
Example ¶
ExampleForCommand demonstrates probing a container by running a command inside it. ForCommand reports ready as soon as the command exits with code 0.
err := Wait(context.Background(), &fakeTarget{}, ForCommand([]string{"echo", "ok"}))
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
fmt.Println("ready")
Output: ready
func ForHTTPGet ¶
ForHTTPGet returns a strategy that is ready when an HTTP request to the container's internal port returns an accepted status (default 2xx, overridable via WithResponseStatuses) and, if WithBodyContains is set, a body containing the substring.
"connection refused" (server not listening yet) and unaccepted statuses are transient retries. An invalid URL or a TLS handshake failure is fatal, as is t.URL reporting the port as not registered.
Example ¶
ExampleForHTTPGet demonstrates probing a container by making an HTTP request to one of its ports. ForHTTPGet reports ready once the probe returns an accepted status on the requested path.
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
defer srv.Close()
host, portStr, err := net.SplitHostPort(srv.Listener.Addr().String())
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
port, err := strconv.Atoi(portStr)
if err != nil || port < 0 || port > int(^uint16(0)) {
fmt.Printf("error: invalid port %q\n", portStr)
return
}
t := &fakeTarget{hostPort: &docker.HostPort{Host: host, Port: uint16(port)}}
err = Wait(
context.Background(),
t,
ForHTTPGet(uint16(port), WithPath("/health"), WithResponseStatuses(http.StatusOK)),
)
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
fmt.Println("ready")
Output: ready
func ForLog ¶
ForLog returns a strategy that is ready when some line of container output matches m. The Target must implement the optional GetOutput capability; otherwise ForLog returns a fatal error.
Example ¶
ExampleForLog demonstrates matching a line of container output. ForLog is ready once some line matches the given matcher. It requires a Target that implements the optional GetOutput capability (docker.Container and docker.TestContainer both do).
t := &logFakeTarget{
fakeTarget: &fakeTarget{},
lines: []string{"started", "READY"},
}
err := Wait(
context.Background(),
t,
ForLog(docker.NewSubstringMatcher("READY")),
)
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
fmt.Println("ready")
Output: ready
func ForTCPConnection ¶
ForTCPConnection returns a strategy that is ready when a TCP connection to the container's internal port can be established (and is immediately closed). This is a pure liveness probe: it proves the listener accepts connections but says nothing about application readiness.
A refused/unreachable connect is a transient retry; t.URL reporting the port as not registered is fatal. UDP is not supported: there is no acknowledgement on a UDP "connection", so a connect-style liveness probe is meaningless.
Example ¶
ExampleForTCPConnection demonstrates a pure TCP liveness probe: ForTCPConnection reports ready once a TCP connection to the container's internal port can be established.
// A listener that accepts connections immediately, acting as the "service".
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
defer func() { _ = l.Close() }()
_, portStr, err := net.SplitHostPort(l.Addr().String())
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
port, err := strconv.Atoi(portStr)
if err != nil || port < 0 || port > int(^uint16(0)) {
fmt.Printf("error: invalid port %q\n", portStr)
return
}
// ForTCPConnection dials the listener's own port via t.URL.
t := &fakeTarget{hostPort: &docker.HostPort{Host: "127.0.0.1", Port: uint16(port)}}
err = Wait(context.Background(), t, ForTCPConnection(uint16(port)))
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
fmt.Println("ready")
Output: ready
type Target ¶
type Target interface {
URL(proto docker.Protocol, port uint16) (*docker.HostPort, error)
Exec(ctx context.Context, cmd []string) (*docker.ExecResult, error)
}
Target is the minimal capability a strategy needs from a container. Both docker.Container and docker.TestContainer satisfy it directly.