Documentation
¶
Overview ¶
Package tcpwait waits until network endpoints or paths are ready.
Overview ¶
Use Wait / WaitWithOptions to block until every Check succeeds (typical container pre-start / initContainer). Use Watch / WatchWithOptions to keep probing and log up/down/blip transitions (upgrade windows, firewall opens) until the context is cancelled.
Supported probe kinds (prefer constructors over filling Check by hand):
- TCP — host:port accepts a connection
- HTTP / HTTPStatus / HTTPWithHeaders — GET returns an expected status (default 200)
- Redis — RESP PING → PONG
- Postgres — StartupMessage answered (auth or non-startup error)
- File — filesystem path exists
CLI ¶
A command-line tool is provided at github.com/donkeyx/tcp-wait/v2/cmd/tcp-wait:
go install github.com/donkeyx/tcp-wait/v2/cmd/tcp-wait@latest
Errors ¶
Wait returns an error wrapping ErrTimeout when the context deadline fires before all checks pass. Use errors.Is(err, tcpwait.ErrTimeout). The message lists checks that never succeeded.
Watch returns ctx.Err() when stopped (context.Canceled or DeadlineExceeded) and a WatchResult with the last known state of each check.
Index ¶
- Variables
- func Wait(ctx context.Context, checks []Check) error
- func WaitTCP(ctx context.Context, addrs []string) error
- func WaitTCPWithOptions(ctx context.Context, addrs []string, opts Options) error
- func WaitWithOptions(ctx context.Context, checks []Check, opts Options) error
- type Check
- func File(path string) Check
- func HTTP(url string) Check
- func HTTPStatus(url string, status int) Check
- func HTTPStatusWithHeaders(url string, status int, headers map[string]string) Check
- func HTTPWithHeaders(url string, headers map[string]string) Check
- func Postgres(addr string, userDB ...string) Check
- func Redis(addr string) Check
- func TCP(addr string) Check
- func TCPAddrs(addrs ...string) []Check
- type Kind
- type Options
- type WatchResult
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrTimeout = errors.New("services did not respond")
ErrTimeout is returned when the context deadline is hit before all checks pass. Use errors.Is(err, ErrTimeout). The error string lists checks that never succeeded.
Functions ¶
func Wait ¶
Wait runs all checks in parallel until each succeeds or ctx is done. Defaults: 1s dial timeout, 1s retry interval, no logging.
Example ¶
ExampleWait shows building checks and waiting until they pass (or time out).
package main
import (
"context"
"errors"
"fmt"
"time"
tcpwait "github.com/donkeyx/tcp-wait/v2"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
defer cancel()
err := tcpwait.Wait(ctx, []tcpwait.Check{
// tcpwait.TCP("db:5432"),
// tcpwait.Redis("cache:6379"),
// tcpwait.Postgres("db:5432"),
// tcpwait.HTTP("http://api:8080/readyz"),
// tcpwait.File("/var/run/app.ready"),
tcpwait.TCP("127.0.0.1:1"), // nothing listening — will time out in this example
})
if errors.Is(err, tcpwait.ErrTimeout) {
fmt.Println("timed out")
return
}
if err != nil {
fmt.Println("error:", err)
return
}
fmt.Println("ready")
}
Output: timed out
func WaitTCP ¶
WaitTCP waits until each host:port accepts a TCP connection. Convenience wrapper around Wait + TCPAddrs.
Example ¶
ExampleWaitTCP is a shortcut when you only need TCP accept checks.
package main
import (
"context"
"fmt"
"time"
tcpwait "github.com/donkeyx/tcp-wait/v2"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
defer cancel()
if err := tcpwait.WaitTCP(ctx, []string{"127.0.0.1:1"}); err != nil {
fmt.Println("not ready")
return
}
fmt.Println("ready")
}
Output: not ready
func WaitTCPWithOptions ¶
WaitTCPWithOptions is WaitTCP with options.
Types ¶
type Check ¶
type Check struct {
Kind Kind
Target string // host:port for tcp/redis/postgres; URL for http; path for file
// HTTP: expected status code (default 200).
ExpectStatus int
// HTTP request headers.
Headers map[string]string
// Postgres: StartupMessage user/database (defaults: postgres/postgres).
Username string
Database string
}
Check is a single readiness probe. Prefer constructors (TCP, HTTP, Redis, Postgres, File) over filling the struct by hand.
func File ¶
File waits until path exists (regular file, socket, directory, etc.). Useful for readiness marker files or unix socket paths.
func HTTPStatus ¶
HTTPStatus is like HTTP but with an explicit expected status code.
func HTTPStatusWithHeaders ¶ added in v2.1.0
HTTPStatusWithHeaders is like HTTPStatus with request headers.
func HTTPWithHeaders ¶ added in v2.1.0
HTTPWithHeaders waits until GET url returns 200 with the supplied headers.
func Postgres ¶
Postgres waits until PostgreSQL at addr answers a StartupMessage (auth request or a non-"starting up" error). Optional user/database default to "postgres".
type Options ¶
type Options struct {
// DialTimeout is how long each probe attempt may take (dial / HTTP client / etc).
// Default: 1s.
DialTimeout time.Duration
// Interval is how long to wait between failed attempts for a check. Default: 1s.
Interval time.Duration
// Logger receives per-attempt warnings and per-check ready messages.
// Nil discards logs.
Logger *slog.Logger
// TLS enables TLS for TCP and Redis probes, and configures HTTPS probes.
TLS bool
// TLSServerName overrides the server name used for certificate verification.
TLSServerName string
// TLSInsecureSkipVerify disables certificate verification. Use only for
// trusted development or private test endpoints.
TLSInsecureSkipVerify bool
}
Options configure Wait. Zero values use defaults.
type WatchResult ¶
type WatchResult struct {
// Up maps check label (Check.String()) → last successful probe.
Up map[string]bool
}
WatchResult is the last known state of each check when Watch stops.
func Watch ¶
func Watch(ctx context.Context, checks []Check) (WatchResult, error)
Watch repeatedly probes all checks until ctx is cancelled. It logs only state transitions:
INFO "check up" — first success, or recovery after down WARN "check down" — first failure while never up yet WARN "check blip" — transition from up → down (the upgrade hole)
Defaults match Wait (1s dial, 1s interval, quiet logger). The returned error is usually ctx.Err() (Canceled / DeadlineExceeded).
Example ¶
ExampleWatch shows continuous monitoring (upgrade / firewall blips). Cancel the context (timeout or signal) to stop; inspect WatchResult.
package main
import (
"context"
"fmt"
"time"
tcpwait "github.com/donkeyx/tcp-wait/v2"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 400*time.Millisecond)
defer cancel()
res, err := tcpwait.WatchWithOptions(ctx, []tcpwait.Check{
tcpwait.TCP("127.0.0.1:1"),
}, tcpwait.Options{
Interval: 100 * time.Millisecond,
DialTimeout: 50 * time.Millisecond,
})
_ = err // typically context.DeadlineExceeded
if res.AllUp() {
fmt.Println("ended all up")
} else {
fmt.Println("ended degraded")
}
}
Output: ended degraded
func WatchWithOptions ¶
WatchWithOptions is Watch with explicit options.
func (WatchResult) AllUp ¶
func (r WatchResult) AllUp() bool
AllUp reports whether every check was up on the last probe.
func (WatchResult) Down ¶
func (r WatchResult) Down() []string
Down returns labels that were down on the last probe (sorted).