Documentation
¶
Index ¶
- func ForListeningPort(port string) *waitForPort
- func WaitForAll(strategies ...WaitStrategy) *multiWait
- func WaitForFunc(fn func(ctx context.Context, target ContainerTarget) error) *waitFunc
- func WaitForHTTP(port, path string, expectedStatus int) *waitForHTTP
- func WaitForHealthy() *waitForHealthy
- func WaitForLog(pattern string) *waitForLog
- func WaitForPort(port string) *waitForPort
- type ContainerRequest
- type ContainerState
- type ContainerTarget
- type Executor
- func (e *Executor) Close() error
- func (e *Executor) ConnectionString(ctx context.Context, containerPort, template string) (string, error)
- func (e *Executor) ContainerID() string
- func (e *Executor) Endpoint(ctx context.Context, containerPort string) (string, error)
- func (e *Executor) ExitCode(ctx context.Context) (int, error)
- func (e *Executor) FollowLogs(ctx context.Context, w io.Writer, opts ...LogOption) error
- func (e *Executor) GetAllPorts(ctx context.Context) (map[string]string, error)
- func (e *Executor) GetIPAddress(ctx context.Context, network string) (string, error)
- func (e *Executor) GetLastNLines(ctx context.Context, n int) (string, error)
- func (e *Executor) GetLogsSince(ctx context.Context, since string) (string, error)
- func (e *Executor) GetNetworks(ctx context.Context) ([]string, error)
- func (e *Executor) GetStats(ctx context.Context) (container.StatsResponseReader, error)
- func (e *Executor) GetStderr(ctx context.Context) (string, error)
- func (e *Executor) GetStdout(ctx context.Context) (string, error)
- func (e *Executor) HealthCheck(ctx context.Context) (*HealthStatus, error)
- func (e *Executor) Host(_ context.Context) (string, error)
- func (e *Executor) Inspect(ctx context.Context) (*container.InspectResponse, error)
- func (e *Executor) IsRunning(ctx context.Context) (bool, error)
- func (e *Executor) Logs(ctx context.Context, opts ...LogOption) (string, error)
- func (e *Executor) MappedPort(ctx context.Context, containerPort string) (string, error)
- func (e *Executor) Restart(ctx context.Context) error
- func (e *Executor) Start(ctx context.Context) error
- func (e *Executor) Status(ctx context.Context) (*Status, error)
- func (e *Executor) Stop(ctx context.Context) error
- func (e *Executor) StreamLogs(ctx context.Context, opts ...LogOption) (<-chan LogEntry, <-chan error)
- func (e *Executor) Terminate(ctx context.Context) error
- func (e *Executor) Wait(ctx context.Context) (int64, error)
- func (e *Executor) WaitForState(ctx context.Context, targetState string, timeout time.Duration) error
- func (e *Executor) WaitHealthy(ctx context.Context, timeout time.Duration) error
- type HealthLog
- type HealthStatus
- type LogEntry
- type LogOption
- type Option
- func WithAutoRemove(autoRemove bool) Option
- func WithCapAdd(caps ...string) Option
- func WithCapDrop(caps ...string) Option
- func WithCmd(cmd ...string) Option
- func WithEntrypoint(entrypoint ...string) Option
- func WithEnv(env string) Option
- func WithEnvMap(env map[string]string) Option
- func WithExposedPorts(ports ...string) Option
- func WithHostname(hostname string) Option
- func WithImage(image string) Option
- func WithLabel(key, value string) Option
- func WithLabels(labels map[string]string) Option
- func WithName(name string) Option
- func WithNetwork(network string) Option
- func WithNetworkMode(mode string) Option
- func WithNetworks(networks ...string) Option
- func WithOTelConfig(otelConfig *otel.Config) Option
- func WithPortBindings(bindings map[string]string) Option
- func WithPorts(portMapping string) Option
- func WithPrivileged(privileged bool) Option
- func WithRequest(req ContainerRequest) Option
- func WithShmSize(size int64) Option
- func WithTimeout(timeout time.Duration) Option
- func WithTmpfs(path, options string) Option
- func WithUser(user string) Option
- func WithVolume(hostPath, containerPath string) Option
- func WithVolumeRO(hostPath, containerPath string) Option
- func WithVolumes(volumes map[string]string) Option
- func WithWaitStrategy(strategy WaitStrategy) Option
- func WithWorkDir(workDir string) Option
- type Status
- type WaitStrategy
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ForListeningPort ¶
func ForListeningPort(port string) *waitForPort
ForListeningPort is an alias for WaitForPort for testcontainers compatibility.
func WaitForAll ¶
func WaitForAll(strategies ...WaitStrategy) *multiWait
WaitForAll creates a wait strategy that waits for all strategies to pass.
func WaitForFunc ¶
func WaitForFunc(fn func(ctx context.Context, target ContainerTarget) error) *waitFunc
WaitForFunc creates a wait strategy from a custom function. This allows users to implement their own wait logic.
Example ¶
WaitForFunc wraps arbitrary readiness logic. The strategy receives a ContainerTarget exposing the container ID, a log stream, and a projected runtime state — no Docker client import required.
Output is non-deterministic; compile-checked only.
package main
import (
"context"
"fmt"
"time"
"github.com/jasoet/pkg/v3/docker"
)
func main() {
strategy := docker.WaitForFunc(func(ctx context.Context, target docker.ContainerTarget) error {
state, err := target.State(ctx)
if err != nil {
return err
}
if !state.Running {
return fmt.Errorf("container %s not running", target.ID())
}
return nil
}).WithStartupTimeout(30 * time.Second)
exec, err := docker.New(
docker.WithImage("redis:7-alpine"),
docker.WithWaitStrategy(strategy),
)
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = exec.Close() }()
}
Output:
func WaitForHTTP ¶
WaitForHTTP creates a wait strategy that waits for an HTTP endpoint. Port format: "8080" or "8080/tcp" Path: HTTP path (e.g., "/health") Expected status: HTTP status code (e.g., 200)
func WaitForHealthy ¶
func WaitForHealthy() *waitForHealthy
WaitForHealthy creates a wait strategy that waits for health check to pass. Container must have a HEALTHCHECK defined in Dockerfile or via Docker API.
func WaitForLog ¶
func WaitForLog(pattern string) *waitForLog
WaitForLog creates a wait strategy that waits for a log pattern. Pattern can be a simple string or regex pattern. If the pattern is invalid, WaitUntilReady will return an error instead of panicking.
Example ¶
WaitForLog creates a strategy that blocks Start until a line matching the pattern appears in the container logs. The pattern is a regular expression; plain strings work because they are valid regexes.
Output is non-deterministic; compile-checked only.
package main
import (
"context"
"fmt"
"time"
"github.com/jasoet/pkg/v3/docker"
)
func main() {
strategy := docker.WaitForLog("database system is ready to accept connections").
WithStartupTimeout(60 * time.Second)
exec, err := docker.New(
docker.WithImage("postgres:18-alpine"),
docker.WithEnvMap(map[string]string{
"POSTGRES_PASSWORD": "secret",
}),
docker.WithWaitStrategy(strategy),
)
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = exec.Close() }()
// Start blocks until the log pattern matches (requires a Docker daemon):
//
// ctx := context.Background()
// if err := exec.Start(ctx); err != nil { ... }
// defer exec.Terminate(ctx)
_ = context.Background()
}
Output:
func WaitForPort ¶
func WaitForPort(port string) *waitForPort
WaitForPort creates a wait strategy that waits for a port to be listening. Port format: "8080/tcp" or just "8080" (defaults to tcp).
Types ¶
type ContainerRequest ¶
type ContainerRequest struct {
// Image is the container image to use (e.g., "nginx:latest")
Image string
// Name is the container name (optional)
Name string
// Hostname sets the container hostname
Hostname string
// ExposedPorts are ports to expose (e.g., []string{"80/tcp", "443/tcp"})
ExposedPorts []string
// Env is a map of environment variables
Env map[string]string
// Cmd overrides the default command
Cmd []string
// Entrypoint overrides the default entrypoint
Entrypoint []string
// WorkingDir sets the working directory
WorkingDir string
// User sets the user (e.g., "1000:1000" or "username")
User string
// Labels are container labels
Labels map[string]string
// Volumes maps host paths to container paths
// Key: host path, Value: container path
Volumes map[string]string
// BindMounts allows raw Docker bind mount specs. Use with caution: callers are responsible for
// ensuring mount paths do not expose sensitive host directories.
// Key: container path, Value: bind mount spec
BindMounts map[string]string
// Networks to attach the container to
Networks []string
// NetworkMode sets the network mode (bridge, host, none, container:<name>)
NetworkMode string
// PortBindings maps container ports to host ports
// Key: container port (e.g., "80/tcp"), Value: host port (e.g., "8080")
PortBindings map[string]string
// AutoRemove automatically removes the container when it stops
AutoRemove bool
// Privileged runs the container in privileged mode
Privileged bool
// CapAdd adds Linux capabilities
CapAdd []string
// CapDrop drops Linux capabilities
CapDrop []string
// Tmpfs mounts tmpfs filesystems
// Key: container path, Value: options
Tmpfs map[string]string
// ShmSize sets the size of /dev/shm
ShmSize int64
// WaitingFor specifies the wait strategy for container readiness
WaitingFor WaitStrategy
// Timeout for container operations (default: 30s)
Timeout time.Duration
// OTelConfig enables OpenTelemetry instrumentation (optional)
OTelConfig *otel.Config `yaml:"-" mapstructure:"-"`
}
ContainerRequest represents a declarative container configuration, similar to testcontainers.ContainerRequest. This allows users to configure containers using a struct-based approach.
type ContainerState ¶
type ContainerState struct {
Running bool
HealthStatus string // "" when no healthcheck is defined
Ports map[string][]string // containerPort ("80/tcp") → hostPorts
}
ContainerState is a snapshot of a container's runtime state, projected from ContainerInspect into a library-owned type.
type ContainerTarget ¶
type ContainerTarget struct {
// contains filtered or unexported fields
}
ContainerTarget is the runtime surface a WaitStrategy can inspect. It wraps the Docker client and container ID internally so that strategies never need to import the docker client.
A ContainerTarget is only usable when constructed by the Executor (as passed to WaitStrategy.WaitUntilReady). The zero value holds a nil client and will panic on Logs and State.
func (ContainerTarget) Host ¶
func (t ContainerTarget) Host() string
Host returns a reachable host for the container's published ports, derived from the Docker daemon host. It is "localhost" for local transports (unix, npipe) and the daemon hostname for remote transports (tcp://, ssh://), so port/HTTP wait strategies probe the correct address against a remote daemon.
func (ContainerTarget) Logs ¶
func (t ContainerTarget) Logs(ctx context.Context) (io.ReadCloser, error)
Logs streams the container's stdout and stderr (follow mode). The caller is responsible for closing the returned reader.
func (ContainerTarget) State ¶
func (t ContainerTarget) State(ctx context.Context) (ContainerState, error)
State inspects the container and projects the result into a ContainerState.
A nil inspect.State is treated as an error: it indicates an abnormal inspect response, and strategies polling for Running/HealthStatus would otherwise spin on a meaningless zero state until timeout. A nil NetworkSettings is tolerated (e.g., containers without networking) and yields an empty Ports map.
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor manages a Docker container lifecycle.
func New ¶
New creates a new Docker executor with functional options.
Example with functional options:
exec := docker.New(
docker.WithImage("nginx:latest"),
docker.WithPorts("80:8080"),
docker.WithEnv("KEY=value"),
)
Example with ContainerRequest struct:
req := docker.ContainerRequest{
Image: "nginx:latest",
ExposedPorts: []string{"80/tcp"},
Env: map[string]string{"KEY": "value"},
}
exec := docker.New(docker.WithRequest(req))
Example combining both:
req := docker.ContainerRequest{Image: "nginx:latest"}
exec := docker.New(
docker.WithRequest(req),
docker.WithOTelConfig(otelCfg), // Add observability
)
Example ¶
New assembles an executor from functional options. Constructing the executor only validates configuration and creates a Docker client handle; no container is started until Start is called.
package main
import (
"fmt"
"github.com/jasoet/pkg/v3/docker"
)
func main() {
exec, err := docker.New(
docker.WithImage("nginx:latest"),
docker.WithPorts("80:0"), // host port auto-assigned
docker.WithEnv("ENV=production"),
docker.WithAutoRemove(true),
)
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = exec.Close() }()
fmt.Println("executor created, container started:", exec.ContainerID() != "")
}
Output: executor created, container started: false
func NewFromRequest ¶
func NewFromRequest(req ContainerRequest, opts ...Option) (*Executor, error)
NewFromRequest creates a new Docker executor from a ContainerRequest struct. Additional options can be passed to override or extend the request configuration.
Example with just struct:
req := docker.ContainerRequest{
Image: "nginx:latest",
Env: map[string]string{"KEY": "value"},
}
exec, err := docker.NewFromRequest(req)
Example with additional options (options override struct fields):
req := docker.ContainerRequest{
Image: "nginx:latest",
Name: "default-name",
}
exec, err := docker.NewFromRequest(req,
docker.WithName("override-name"), // Overrides struct name
docker.WithOTelConfig(otelCfg), // Adds observability
docker.WithPorts("80:8080"), // Adds port mapping
)
Example ¶
NewFromRequest is sugar over New(WithRequest(req), ...): it builds an executor from a ContainerRequest struct and lets trailing options override individual struct fields.
package main
import (
"fmt"
"time"
"github.com/jasoet/pkg/v3/docker"
)
func main() {
req := docker.ContainerRequest{
Image: "postgres:18-alpine",
ExposedPorts: []string{"5432/tcp"},
Env: map[string]string{
"POSTGRES_PASSWORD": "secret",
},
WaitingFor: docker.WaitForLog("ready to accept connections").
WithStartupTimeout(60 * time.Second),
}
// WithName overrides the (empty) struct field; options always win.
exec, err := docker.NewFromRequest(req, docker.WithName("my-postgres"))
if err != nil {
fmt.Println("error:", err)
return
}
defer func() { _ = exec.Close() }()
fmt.Println("executor created from request")
}
Output: executor created from request
func (*Executor) Close ¶
Close closes the Docker client connection. The container is NOT terminated automatically - call Terminate() first if needed. After Close(), any method that uses the Docker client will return an error.
func (*Executor) ConnectionString ¶
func (e *Executor) ConnectionString(ctx context.Context, containerPort, template string) (string, error)
ConnectionString builds a connection string for the container. This is useful for database containers. Use {{endpoint}} as the placeholder for the host:port value.
Example:
// For PostgreSQL
connStr, err := exec.ConnectionString(ctx, "5432/tcp", "postgres://user:pass@{{endpoint}}/db")
// Result: "postgres://user:pass@localhost:32768/db"
func (*Executor) ContainerID ¶
ContainerID returns the Docker container ID. Returns empty string if container hasn't been started yet.
func (*Executor) Endpoint ¶
Endpoint returns the full endpoint address (host:port) for a container port. This is a convenience method combining Host() and MappedPort().
Example:
endpoint, err := exec.Endpoint(ctx, "80/tcp")
// endpoint might be "localhost:32768"
// Use it directly with HTTP client
resp, err := http.Get("http://" + endpoint + "/health")
func (*Executor) ExitCode ¶
ExitCode retrieves the container exit code. Returns an error if the container hasn't exited yet.
func (*Executor) FollowLogs ¶
FollowLogs streams container logs to the provided writer. This is useful for piping logs to stdout or a file.
Example:
err := exec.FollowLogs(ctx, os.Stdout)
func (*Executor) GetAllPorts ¶
GetAllPorts returns all exposed ports and their mappings. Returns a map of container ports to host ports.
Example output:
{
"80/tcp": "8080",
"443/tcp": "8443",
}
func (*Executor) GetIPAddress ¶
GetIPAddress returns the container's IP address in a specific network. If network is empty, returns the IP from the first available network.
func (*Executor) GetLastNLines ¶
GetLastNLines retrieves the last N lines of logs.
func (*Executor) GetLogsSince ¶
GetLogsSince retrieves logs since a specific time. Time can be RFC3339 timestamp or duration string (e.g., "10m", "1h").
func (*Executor) GetNetworks ¶
GetNetworks returns all networks the container is connected to.
func (*Executor) GetStats ¶
GetStats retrieves container resource usage statistics. This includes CPU, memory, network, and disk I/O stats. Returns the stats response reader which can be read and decoded by the caller. Remember to close the response body after reading.
func (*Executor) HealthCheck ¶
func (e *Executor) HealthCheck(ctx context.Context) (*HealthStatus, error)
HealthCheck retrieves the current health status. Returns an error if health check is not configured for the container.
func (*Executor) Host ¶
Host returns the container host address. For local Docker/Podman this is "localhost"; for a remote daemon (DOCKER_HOST set to tcp:// or ssh://) it is the daemon's hostname, since published ports are reachable on the daemon host, not the client.
func (*Executor) Inspect ¶
Inspect returns the full container inspection details. This provides access to all container metadata.
func (*Executor) Logs ¶
Logs returns all container logs as a string. Use LogOptions for more control.
func (*Executor) MappedPort ¶
MappedPort returns the host port mapped to a container port. Port format: "8080/tcp" or "8080" (defaults to tcp).
Example:
hostPort, err := exec.MappedPort(ctx, "80/tcp") // hostPort might be "32768" (randomly assigned by Docker)
func (*Executor) Restart ¶
Restart restarts the container. Note: There is a small TOCTOU window between the containerID check and the Docker API call. Concurrent Terminate() may cause a benign "container not found" error.
func (*Executor) Start ¶
Start pulls the image (if needed), creates and starts the container. If a wait strategy is configured, it blocks until the container is ready.
func (*Executor) Stop ¶
Stop gracefully stops the container (sends SIGTERM). The container can still be restarted after stopping. Note: There is a small TOCTOU window between the containerID check and the Docker API call. Concurrent Terminate() may cause a benign "container not found" error.
func (*Executor) StreamLogs ¶
func (e *Executor) StreamLogs(ctx context.Context, opts ...LogOption) (<-chan LogEntry, <-chan error)
StreamLogs streams container logs to a channel.
The context MUST be cancelable: streaming (especially with WithFollow) blocks until the container's log stream ends or ctx is canceled. Cancel ctx when done to release the background goroutine and underlying connection; abandoning the returned channels without canceling ctx leaks both. The error channel is buffered and closed alongside the log channel, so it is safe to ignore.
Docker's multiplexed stream is demultiplexed with stdcopy, so stdout and stderr frames are labeled correctly and malformed frame sizes cannot trigger huge allocations.
func (*Executor) Terminate ¶
Terminate forcefully stops and removes the container. This is a destructive operation and the container cannot be restarted.
type HealthLog ¶
type HealthLog struct {
// Start is when the check started
Start time.Time
// End is when the check completed
End time.Time
// ExitCode is the health check exit code
ExitCode int
// Output is the health check output
Output string
}
HealthLog represents a single health check result.
type HealthStatus ¶
type HealthStatus struct {
// Status is the health status (healthy, unhealthy, starting)
Status string
// FailingStreak is the number of consecutive failures
FailingStreak int
// Log contains recent health check results
Log []HealthLog
}
HealthStatus represents container health check status.
type LogEntry ¶
type LogEntry struct {
// Stream identifies the source (stdout or stderr)
Stream string
// Content is the log line content
Content string
}
LogEntry represents a single log entry from the container.
type LogOption ¶
type LogOption func(*logOptions)
LogOption is a functional option for log retrieval.
func WithFollow ¶
func WithFollow() LogOption
WithFollow streams logs in real-time (default: false).
func WithStderr ¶
WithStderr includes stderr in the logs (default: true).
func WithStdout ¶
WithStdout includes stdout in the logs (default: true).
func WithTail ¶
WithTail limits the number of lines from the end of the logs. Use "all" for all logs, or a number like "100" for last 100 lines.
func WithTimestamps ¶
func WithTimestamps() LogOption
WithTimestamps includes timestamps in log entries (default: false).
type Option ¶
type Option func(*config) error
Option is a functional option for configuring the executor.
func WithAutoRemove ¶
WithAutoRemove automatically removes the container when it stops.
func WithCapAdd ¶
WithCapAdd adds Linux capabilities to the container. WARNING: Adding capabilities increases the container's privileges. Only add capabilities that are strictly required.
func WithEntrypoint ¶
WithEntrypoint sets the container entrypoint.
func WithEnvMap ¶
WithEnvMap sets environment variables from a map.
func WithExposedPorts ¶
WithExposedPorts exposes ports without binding to host.
func WithHostname ¶
WithHostname sets the container hostname.
func WithLabels ¶
WithLabels sets multiple container labels.
func WithNetwork ¶
WithNetwork attaches the container to a network.
func WithNetworkMode ¶
WithNetworkMode sets the network mode (bridge, host, none, container:<name>).
func WithNetworks ¶
WithNetworks attaches the container to multiple networks.
func WithOTelConfig ¶
WithOTelConfig enables OpenTelemetry instrumentation.
func WithPortBindings ¶
WithPortBindings sets detailed port bindings. Key: container port with protocol (e.g., "80/tcp") Value: host port (e.g., "8080")
func WithPorts ¶
WithPorts adds port mappings in "containerPort:hostPort" format (e.g., "80:8080"). Protocol defaults to TCP. Use "80:8080/udp" for UDP.
func WithPrivileged ¶
WithPrivileged runs the container in privileged mode. WARNING: Privileged containers have full access to the host. Only use with trusted images.
func WithRequest ¶
func WithRequest(req ContainerRequest) Option
WithRequest creates an option from a ContainerRequest struct. This allows combining struct-based and functional options:
req := docker.ContainerRequest{Image: "nginx:latest", ...}
exec := docker.New(
docker.WithRequest(req),
docker.WithOTelConfig(otelCfg), // Additional options
)
func WithShmSize ¶
WithShmSize sets the size of /dev/shm in bytes.
func WithTimeout ¶
WithTimeout sets the timeout for container operations.
func WithVolume ¶
WithVolume mounts a volume. Format: "hostPath:containerPath" or "hostPath:containerPath:ro"
func WithVolumeRO ¶
WithVolumeRO mounts a read-only volume.
func WithVolumes ¶
WithVolumes sets multiple volume mounts.
func WithWaitStrategy ¶
func WithWaitStrategy(strategy WaitStrategy) Option
WithWaitStrategy sets the wait strategy for container readiness.
type Status ¶
type Status struct {
// ID is the container ID
ID string
// Name is the container name
Name string
// Image is the container image
Image string
// State is the container state (running, exited, etc.)
State string
// Status is the detailed status string
Status string
// Running indicates if the container is running
Running bool
// Paused indicates if the container is paused
Paused bool
// Restarting indicates if the container is restarting
Restarting bool
// ExitCode is the exit code (only valid if container has exited)
ExitCode int
// Error contains any error message from the container
Error string
// StartedAt is when the container started
StartedAt time.Time
// FinishedAt is when the container finished (only if stopped)
FinishedAt time.Time
// Health is the health check status (if configured)
Health *HealthStatus
}
Status represents the container status information.
type WaitStrategy ¶
type WaitStrategy interface {
// WaitUntilReady blocks until the container is ready or timeout occurs.
WaitUntilReady(ctx context.Context, target ContainerTarget) error
}
WaitStrategy defines how to wait for a container to be ready.