docker

package
v2.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 24, 2025 License: MIT Imports: 19 Imported by: 0

README

Docker Executor

Go Reference

Simple, flexible Docker container executor inspired by testcontainers API. Run Docker containers with maximum configurability and easy log/status gathering.

Overview

The docker package provides production-ready Docker container management with two API styles:

  • Functional options - Flexible, chainable configuration
  • Struct-based - Testcontainers-compatible declarative style

Features

  • Dual API Design: Choose between functional options or struct-based configuration
  • Lifecycle Management: Start, Stop, Restart, Terminate, Wait
  • Wait Strategies: Log patterns, port listening, HTTP health checks, custom functions
  • Log Streaming: Real-time log access with filtering and following
  • Status Monitoring: Container state, health checks, resource stats
  • Network Helpers: Easy access to host, ports, endpoints
  • OpenTelemetry v2: Built-in observability with traces and metrics
  • Production Ready: 83.9% test coverage, zero lint issues
  • Simple & Powerful: Easy for simple cases, flexible for complex scenarios

Installation

go get github.com/jasoet/pkg/v2/docker

Quick Start

Functional Options Style
package main

import (
    "context"
    "github.com/jasoet/pkg/v2/docker"
)

func main() {
    // Create executor with functional options
    exec, _ := docker.New(
        docker.WithImage("nginx:latest"),
        docker.WithPorts("80:8080"),
        docker.WithEnv("ENV=production"),
        docker.WithAutoRemove(true),
    )

    // Start container
    ctx := context.Background()
    exec.Start(ctx)
    defer exec.Terminate(ctx)

    // Get endpoint
    endpoint, _ := exec.Endpoint(ctx, "80/tcp")
    // Use: http://localhost:8080
}
Struct-Based Style (Testcontainers-like)
package main

import (
    "context"
    "github.com/jasoet/pkg/v2/docker"
    "time"
)

func main() {
    // Create executor with ContainerRequest
    req := docker.ContainerRequest{
        Image:        "postgres:16-alpine",
        ExposedPorts: []string{"5432/tcp"},
        Env: map[string]string{
            "POSTGRES_PASSWORD": "secret",
            "POSTGRES_USER":     "testuser",
            "POSTGRES_DB":       "testdb",
        },
        WaitingFor: docker.WaitForLog("ready to accept connections").
            WithStartupTimeout(60 * time.Second),
    }

    exec, _ := docker.NewFromRequest(req)

    ctx := context.Background()
    exec.Start(ctx)
    defer exec.Terminate(ctx)

    // Connection string helper
    connStr, _ := exec.ConnectionString(ctx, "5432/tcp",
        "postgres://testuser:secret@%s/testdb")
}
Hybrid Style (Mix Both)

You can combine both styles in two ways:

1. Struct within options:

req := docker.ContainerRequest{
    Image: "nginx:latest",
    ExposedPorts: []string{"80/tcp"},
}

exec, _ := docker.New(
    docker.WithRequest(req),
    docker.WithName("my-nginx"),
    docker.WithOTelConfig(otelCfg), // Add observability
)

2. Options after struct (NEW):

req := docker.ContainerRequest{
    Image: "postgres:16-alpine",
    Env: map[string]string{
        "POSTGRES_PASSWORD": "secret",
    },
}

// Add additional options that override/extend the struct
exec, _ := docker.NewFromRequest(req,
    docker.WithName("my-postgres"),        // Add name
    docker.WithPorts("5432:15432"),        // Add port mapping
    docker.WithOTelConfig(otelCfg),        // Add observability
)

Note: When using both, later options override earlier ones:

req := docker.ContainerRequest{
    Image: "nginx:latest",
    Name:  "default-name",
}

exec, _ := docker.NewFromRequest(req,
    docker.WithName("override-name"),  // ← This wins!
)

API Styles

Functional Options

Advantages:

  • Chainable and composable
  • Clear and explicit
  • Easy to add/remove options
  • Type-safe with IDE autocomplete

Example:

exec, _ := docker.New(
    docker.WithImage("redis:7-alpine"),
    docker.WithPorts("6379:16379"),
    docker.WithVolume("/data", "/data"),
    docker.WithWaitStrategy(docker.WaitForPort("6379/tcp")),
)
Struct-Based (ContainerRequest)

Advantages:

  • Familiar to testcontainers users
  • Easy to build programmatically
  • Good for configuration files (YAML/JSON)
  • Compact for complex configs

Example:

req := docker.ContainerRequest{
    Image:        "mysql:8",
    ExposedPorts: []string{"3306/tcp"},
    Env: map[string]string{
        "MYSQL_ROOT_PASSWORD": "root",
        "MYSQL_DATABASE":      "app",
    },
    WaitingFor: docker.WaitForLog("ready for connections"),
}
exec, _ := docker.NewFromRequest(req)

Configuration Options

Image & Container
docker.WithImage("nginx:latest")              // Container image
docker.WithName("my-container")               // Container name
docker.WithHostname("app-server")             // Hostname
docker.WithCmd("--verbose", "--debug")        // Override CMD
docker.WithEntrypoint("/bin/sh", "-c")       // Override ENTRYPOINT
docker.WithWorkDir("/app")                    // Working directory
docker.WithUser("1000:1000")                  // User (UID:GID)
Environment
docker.WithEnv("KEY=value")                   // Single env var
docker.WithEnvMap(map[string]string{          // Multiple env vars
    "DB_HOST": "localhost",
    "DB_PORT": "5432",
})
Ports
docker.WithPorts("80:8080")                   // Simple port mapping
docker.WithPorts("443:8443/tcp")              // With protocol
docker.WithPortBindings(map[string]string{    // Multiple ports
    "80/tcp":  "8080",
    "443/tcp": "8443",
})
docker.WithExposedPorts("8080", "9090")       // Expose without binding
Volumes
docker.WithVolume("/host/path", "/container/path")
docker.WithVolumeRO("/host/path", "/container/path") // Read-only
docker.WithVolumes(map[string]string{
    "/host/data": "/data",
    "/host/logs": "/var/log",
})
Network
docker.WithNetwork("my-network")              // Attach to network
docker.WithNetworks("net1", "net2")           // Multiple networks
docker.WithNetworkMode("bridge")              // Network mode
docker.WithNetworkMode("host")                // Host network
Security
docker.WithPrivileged(true)                   // Privileged mode
docker.WithCapAdd("NET_ADMIN", "SYS_TIME")   // Add capabilities
docker.WithCapDrop("CHOWN", "SETUID")        // Drop capabilities
Resources
docker.WithShmSize(67108864)                  // /dev/shm size (64MB)
docker.WithTmpfs("/tmp", "size=64m")         // tmpfs mount
Cleanup
docker.WithAutoRemove(true)                   // Auto-remove on stop
Wait Strategies
docker.WithWaitStrategy(
    docker.WaitForLog("started successfully").
        WithStartupTimeout(60 * time.Second),
)

docker.WithWaitStrategy(
    docker.WaitForPort("8080/tcp"),
)

docker.WithWaitStrategy(
    docker.WaitForHTTP("8080", "/health", 200),
)

docker.WithWaitStrategy(
    docker.WaitForHealthy(),
)

docker.WithWaitStrategy(
    docker.WaitForFunc(func(ctx context.Context, cli *client.Client, id string) error {
        // Custom readiness check
        return nil
    }),
)
Observability
docker.WithOTelConfig(otelCfg)                // OpenTelemetry
docker.WithTimeout(30 * time.Second)          // Operation timeout

Lifecycle Methods

Start
err := exec.Start(ctx)
// - Pulls image if needed
// - Creates container
// - Starts container
// - Waits for readiness (if strategy configured)
Stop
err := exec.Stop(ctx)
// - Sends SIGTERM
// - Waits for graceful shutdown
// - Container can be restarted
Terminate
err := exec.Terminate(ctx)
// - Force stops container
// - Removes container
// - Cannot be restarted
Restart
err := exec.Restart(ctx)
// - Restarts running container
Wait
exitCode, err := exec.Wait(ctx)
// - Blocks until container exits
// - Returns exit code

Logs

Get All Logs
logs, err := exec.Logs(ctx)
Stream Logs
logCh, errCh := exec.StreamLogs(ctx, docker.WithFollow())
for log := range logCh {
    fmt.Println(log.Content)
}
Follow Logs to Writer
err := exec.FollowLogs(ctx, os.Stdout)
Advanced Log Options
logs, err := exec.Logs(ctx,
    docker.WithStdout(true),
    docker.WithStderr(true),
    docker.WithTimestamps(),
    docker.WithTail("100"),        // Last 100 lines
    docker.WithSince("10m"),        // Last 10 minutes
)
Convenience Methods
logs, _ := exec.GetLogsSince(ctx, "5m")
logs, _ := exec.GetLastNLines(ctx, 50)
stdout, _ := exec.GetStdout(ctx)
stderr, _ := exec.GetStderr(ctx)

Status & Monitoring

Get Status
status, err := exec.Status(ctx)
fmt.Println(status.Running)      // true/false
fmt.Println(status.State)        // "running", "exited", etc.
fmt.Println(status.ExitCode)     // Exit code if stopped
fmt.Println(status.Health.Status) // "healthy", "unhealthy", "starting"
Check Running
running, err := exec.IsRunning(ctx)
Get Exit Code
exitCode, err := exec.ExitCode(ctx)
Health Check
health, err := exec.HealthCheck(ctx)
fmt.Println(health.Status)
fmt.Println(health.FailingStreak)
Full Inspection
inspect, err := exec.Inspect(ctx)
// Returns *container.InspectResponse with all details
Resource Stats
stats, err := exec.GetStats(ctx)
// CPU, memory, network, disk I/O
Wait for State
err := exec.WaitForState(ctx, "running", 30*time.Second)
err := exec.WaitForHealthy(ctx, 60*time.Second)

Network Helpers

Get Host
host, err := exec.Host(ctx)
// Returns "localhost" for local Docker
Get Mapped Port
port, err := exec.MappedPort(ctx, "8080/tcp")
// Returns "32768" (example randomly assigned port)
Get Endpoint
endpoint, err := exec.Endpoint(ctx, "8080/tcp")
// Returns "localhost:32768"

// Use directly
resp, _ := http.Get("http://" + endpoint + "/health")
Get All Ports
ports, err := exec.GetAllPorts(ctx)
// map[string]string{
//     "80/tcp": "8080",
//     "443/tcp": "8443",
// }
Get Networks
networks, err := exec.GetNetworks(ctx)
// []string{"bridge", "my-network"}
Get IP Address
ip, err := exec.GetIPAddress(ctx, "bridge")
// "172.17.0.2"
Connection String
connStr, err := exec.ConnectionString(ctx, "5432/tcp",
    "postgres://user:pass@%s/db")
// "postgres://user:pass@localhost:15432/db"

Use Cases

Database Testing
req := docker.ContainerRequest{
    Image: "postgres:16-alpine",
    ExposedPorts: []string{"5432/tcp"},
    Env: map[string]string{
        "POSTGRES_PASSWORD": "test",
        "POSTGRES_USER":     "test",
        "POSTGRES_DB":       "test",
    },
    WaitingFor: docker.WaitForLog("ready to accept connections"),
}

exec, _ := docker.NewFromRequest(req)
exec.Start(ctx)
defer exec.Terminate(ctx)

endpoint, _ := exec.Endpoint(ctx, "5432/tcp")
db, _ := sql.Open("postgres", "postgres://test:test@"+endpoint+"/test")
Web Service Testing
exec, _ := docker.New(
    docker.WithImage("nginx:latest"),
    docker.WithPorts("80:0"), // Random host port
    docker.WithWaitStrategy(
        docker.WaitForHTTP("80", "/", 200),
    ),
)

exec.Start(ctx)
defer exec.Terminate(ctx)

endpoint, _ := exec.Endpoint(ctx, "80/tcp")
resp, _ := http.Get("http://" + endpoint)
Message Queue
exec, _ := docker.New(
    docker.WithImage("rabbitmq:3-management"),
    docker.WithPorts("5672:15672"),
    docker.WithPorts("15672:25672"),
    docker.WithEnvMap(map[string]string{
        "RABBITMQ_DEFAULT_USER": "guest",
        "RABBITMQ_DEFAULT_PASS": "guest",
    }),
    docker.WithWaitStrategy(
        docker.WaitForLog("Server startup complete"),
    ),
)
CI/CD Build Container
exec, _ := docker.New(
    docker.WithImage("golang:1.23"),
    docker.WithVolume(pwd, "/app"),
    docker.WithWorkDir("/app"),
    docker.WithCmd("go", "test", "./..."),
    docker.WithAutoRemove(true),
)

exec.Start(ctx)
exitCode, _ := exec.Wait(ctx)
if exitCode != 0 {
    logs, _ := exec.GetStderr(ctx)
    fmt.Println("Tests failed:", logs)
}
Development Environment
// Redis
redis, _ := docker.New(
    docker.WithImage("redis:7-alpine"),
    docker.WithPorts("6379:6379"),
    docker.WithName("dev-redis"),
)

// PostgreSQL
postgres, _ := docker.New(
    docker.WithImage("postgres:16-alpine"),
    docker.WithPorts("5432:5432"),
    docker.WithName("dev-postgres"),
    docker.WithEnvMap(map[string]string{
        "POSTGRES_PASSWORD": "dev",
    }),
)

redis.Start(ctx)
postgres.Start(ctx)

defer redis.Terminate(ctx)
defer postgres.Terminate(ctx)

Best Practices

1. Always Use defer for Cleanup
exec, _ := docker.New(...)
exec.Start(ctx)
defer exec.Terminate(ctx) // Ensures cleanup
2. Use Wait Strategies
// ✅ Good: Wait for readiness
docker.WithWaitStrategy(
    docker.WaitForLog("ready").WithStartupTimeout(30*time.Second),
)

// ❌ Bad: No wait strategy (race conditions)
3. Set Timeouts
// ✅ Good: Reasonable timeout
docker.WithTimeout(30 * time.Second)

// ❌ Bad: No timeout (hangs forever)
4. Use AutoRemove for Tests
docker.WithAutoRemove(true) // Clean up automatically
5. Handle Errors
if err := exec.Start(ctx); err != nil {
    logs, _ := exec.GetStderr(ctx)
    log.Fatalf("Failed to start: %v\nLogs: %s", err, logs)
}
6. Use Context for Cancellation
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()

exec.Start(ctx) // Will respect context timeout
7. Check Container Health
exec.Start(ctx)

// Verify it's actually working
running, _ := exec.IsRunning(ctx)
if !running {
    status, _ := exec.Status(ctx)
    log.Fatalf("Container failed: %s", status.Error)
}

OpenTelemetry Integration

The docker package includes full OpenTelemetry v2 instrumentation for observability.

import (
    "github.com/jasoet/pkg/v2/otel"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    sdkmetric "go.opentelemetry.io/otel/sdk/metric"
)

// Initialize OTel providers
tp := sdktrace.NewTracerProvider(...)
mp := sdkmetric.NewMeterProvider(...)

otelCfg := &otel.Config{
    TracerProvider: tp,
    MeterProvider:  mp,
}

// Use with executor
exec, _ := docker.New(
    docker.WithImage("nginx:latest"),
    docker.WithOTelConfig(otelCfg),
)

// Automatic instrumentation:
// - Traces: docker.Start, docker.Stop, docker.Terminate, docker.Restart, docker.Wait
// - Metrics:
//   - docker.containers.started
//   - docker.containers.stopped
//   - docker.containers.terminated
//   - docker.containers.restarted
//   - docker.container.errors
// - Error tracking: Errors recorded in both traces and metrics with attributes

Testing

The package has comprehensive test coverage (83.9%) with both unit and integration tests.

# Run all tests (requires Docker)
go test ./docker -v

# With coverage
go test ./docker -cover

# Run specific test
go test ./docker -run TestExecutor_FunctionalOptions -v

# Run benchmarks
go test ./docker -bench=. -benchmem

Test Requirements:

  • Docker daemon running
  • Docker API accessible
  • Internet access (for pulling images)

Examples

See examples/ directory for complete, runnable examples:

  • basic.go - Functional options, struct-based, and hybrid styles
  • database.go - PostgreSQL container with real database operations
  • logs.go - Log streaming, filtering, and following
  • multi_container.go - Running multiple containers (Nginx + Redis)

Run examples:

go run -tags example ./docker/examples/basic.go
go run -tags example ./docker/examples/database.go
go run -tags example ./docker/examples/logs.go
go run -tags example ./docker/examples/multi_container.go

Comparison with Testcontainers

Feature Docker Executor Testcontainers-go
API Style Functional options + Struct Struct-based
Simplicity ⭐⭐⭐⭐⭐ ⭐⭐⭐
Flexibility ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
Dependencies Minimal Many
OTel Support Built-in v2 No
Test Coverage 83.9% N/A
Code Quality Zero lint issues N/A
Learning Curve Low Medium
Use Case General purpose Testing focus

Architecture

Key Components
  • Executor - Main container lifecycle manager
  • Config - Container configuration with functional options
  • Wait Strategies - Readiness checking mechanisms
  • Network - Port mapping and endpoint resolution
  • Logs - Log streaming and filtering
  • Status - Container state monitoring
  • OTel - OpenTelemetry v2 instrumentation
Design Principles
  1. Simple by default, powerful when needed - Easy basic usage, advanced features available
  2. Two API styles - Functional options for Go idioms, structs for testcontainers compatibility
  3. Context-aware - All operations respect context cancellation and timeouts
  4. Observable - Built-in OpenTelemetry v2 support for production monitoring
  5. Well-tested - 83.9% coverage with comprehensive integration tests

Troubleshooting

Container fails to start
if err := exec.Start(ctx); err != nil {
    // Check logs for startup errors
    logs, _ := exec.GetStderr(ctx)
    fmt.Println("Error logs:", logs)

    // Check container status
    status, _ := exec.Status(ctx)
    fmt.Printf("State: %s, Error: %s\n", status.State, status.Error)
}
Port already in use
// Use random port (0)
docker.WithPorts("80:0")  // Host port auto-assigned
Wait strategy timeout
// Increase timeout
docker.WithWaitStrategy(
    docker.WaitForLog("ready").
        WithStartupTimeout(120 * time.Second),  // 2 minutes
)
Image pull fails
// Pull manually first
exec, _ := docker.New(docker.WithImage("myregistry.com/image:tag"))

// Or check pull errors
if err := exec.Start(ctx); err != nil {
    if strings.Contains(err.Error(), "pull") {
        fmt.Println("Image pull failed - check registry credentials")
    }
}
  • otel - OpenTelemetry v2 configuration and utilities
  • config - Configuration management with validation
  • logging - Structured logging with context

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExposedPorts

func ExposedPorts(ports []string) (nat.PortSet, error)

ExposedPorts creates a nat.PortSet from a slice of port strings. This is useful for programmatically building exposed ports.

Example:

ports := docker.ExposedPorts([]string{"80/tcp", "443/tcp"})

func ForListeningPort

func ForListeningPort(port string) *waitForPort

ForListeningPort is an alias for WaitForPort for testcontainers compatibility.

func NatPort

func NatPort(port string) (nat.Port, error)

NatPort is a helper to create a nat.Port from a string. This is useful when working with Docker API types directly.

func PortBindings

func PortBindings(ports map[string]string) (nat.PortMap, error)

PortBindings is a helper to create port bindings from a map. This is useful for programmatically building port configurations.

Example:

bindings := docker.PortBindings(map[string]string{
    "80/tcp": "8080",
    "443/tcp": "8443",
})

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, cli *client.Client, containerID string) error) *waitFunc

WaitForFunc creates a wait strategy from a custom function. This allows users to implement their own wait logic.

func WaitForHTTP

func WaitForHTTP(port, path string, expectedStatus int) *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.

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 provides detailed volume configuration
	// 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
}

ContainerRequest represents a declarative container configuration, similar to testcontainers.ContainerRequest. This allows users to configure containers using a struct-based approach.

type Executor

type Executor struct {
	// contains filtered or unexported fields
}

Executor manages a Docker container lifecycle.

func New

func New(opts ...Option) (*Executor, error)

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
)

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
)

func (*Executor) Close

func (e *Executor) Close() error

Close closes the Docker client connection. The container is NOT terminated automatically - call Terminate() first if needed.

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.

Example:

// For PostgreSQL
connStr, err := exec.ConnectionString(ctx, "5432/tcp", "postgres://user:pass@%s/db")
// Result: "postgres://user:pass@localhost:32768/db"

func (*Executor) ContainerID

func (e *Executor) ContainerID() string

ContainerID returns the Docker container ID. Returns empty string if container hasn't been started yet.

func (*Executor) Endpoint

func (e *Executor) Endpoint(ctx context.Context, containerPort string) (string, error)

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

func (e *Executor) ExitCode(ctx context.Context) (int, error)

ExitCode retrieves the container exit code. Returns an error if the container hasn't exited yet.

func (*Executor) FollowLogs

func (e *Executor) FollowLogs(ctx context.Context, w io.Writer, opts ...LogOption) error

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

func (e *Executor) GetAllPorts(ctx context.Context) (map[string]string, error)

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

func (e *Executor) GetIPAddress(ctx context.Context, network string) (string, error)

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

func (e *Executor) GetLastNLines(ctx context.Context, n int) (string, error)

GetLastNLines retrieves the last N lines of logs.

func (*Executor) GetLogsSince

func (e *Executor) GetLogsSince(ctx context.Context, since string) (string, error)

GetLogsSince retrieves logs since a specific time. Time can be RFC3339 timestamp or duration string (e.g., "10m", "1h").

func (*Executor) GetNetworks

func (e *Executor) GetNetworks(ctx context.Context) ([]string, error)

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) GetStderr

func (e *Executor) GetStderr(ctx context.Context) (string, error)

GetStderr retrieves only stderr logs (excludes stdout).

func (*Executor) GetStdout

func (e *Executor) GetStdout(ctx context.Context) (string, error)

GetStdout retrieves only stdout logs (excludes stderr).

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

func (e *Executor) Host(ctx context.Context) (string, error)

Host returns the container host address. For local Docker, this is typically "localhost". For Docker Machine or remote Docker, this returns the Docker host IP.

func (*Executor) Inspect

func (e *Executor) Inspect(ctx context.Context) (*container.InspectResponse, error)

Inspect returns the full container inspection details. This provides access to all container metadata.

func (*Executor) IsRunning

func (e *Executor) IsRunning(ctx context.Context) (bool, error)

IsRunning checks if the container is currently running.

func (*Executor) Logs

func (e *Executor) Logs(ctx context.Context, opts ...LogOption) (string, error)

Logs returns all container logs as a string. Use LogOptions for more control.

func (*Executor) MappedPort

func (e *Executor) MappedPort(ctx context.Context, containerPort string) (string, error)

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

func (e *Executor) Restart(ctx context.Context) error

Restart restarts the container.

func (*Executor) Start

func (e *Executor) Start(ctx context.Context) error

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) Status

func (e *Executor) Status(ctx context.Context) (*Status, error)

Status retrieves the current container status.

func (*Executor) Stop

func (e *Executor) Stop(ctx context.Context) error

Stop gracefully stops the container (sends SIGTERM). The container can still be restarted after stopping.

func (*Executor) StreamLogs

func (e *Executor) StreamLogs(ctx context.Context, opts ...LogOption) (<-chan LogEntry, <-chan error)

StreamLogs streams container logs to a channel. The channel is closed when streaming completes or context is canceled.

func (*Executor) Terminate

func (e *Executor) Terminate(ctx context.Context) error

Terminate forcefully stops and removes the container. This is a destructive operation and the container cannot be restarted.

func (*Executor) Wait

func (e *Executor) Wait(ctx context.Context) (int64, error)

Wait blocks until the container stops and returns its exit code.

func (*Executor) WaitForHealthy

func (e *Executor) WaitForHealthy(ctx context.Context, timeout time.Duration) error

WaitForHealthy waits for the container to become healthy. Returns an error if the container doesn't have health checks configured.

func (*Executor) WaitForState

func (e *Executor) WaitForState(ctx context.Context, targetState string, timeout time.Duration) error

WaitForState waits for the container to reach a specific state. Valid states: running, paused, restarting, removing, exited, dead

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

	// Timestamp is when the log was generated (if timestamps enabled)
	Timestamp time.Time
}

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 WithSince

func WithSince(since string) LogOption

WithSince shows logs since a timestamp (RFC3339) or duration (e.g., "10m").

func WithStderr

func WithStderr(include bool) LogOption

WithStderr includes stderr in the logs (default: true).

func WithStdout

func WithStdout(include bool) LogOption

WithStdout includes stdout in the logs (default: true).

func WithTail

func WithTail(lines string) LogOption

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).

func WithUntil

func WithUntil(until string) LogOption

WithUntil shows logs until a timestamp (RFC3339) or duration.

type Option

type Option func(*config) error

Option is a functional option for configuring the executor.

func WithAutoRemove

func WithAutoRemove(autoRemove bool) Option

WithAutoRemove automatically removes the container when it stops.

func WithCapAdd

func WithCapAdd(caps ...string) Option

WithCapAdd adds Linux capabilities.

func WithCapDrop

func WithCapDrop(caps ...string) Option

WithCapDrop drops Linux capabilities.

func WithCmd

func WithCmd(cmd ...string) Option

WithCmd sets the container command.

func WithEntrypoint

func WithEntrypoint(entrypoint ...string) Option

WithEntrypoint sets the container entrypoint.

func WithEnv

func WithEnv(env string) Option

WithEnv adds an environment variable in KEY=VALUE format.

func WithEnvMap

func WithEnvMap(env map[string]string) Option

WithEnvMap sets environment variables from a map.

func WithExposedPorts

func WithExposedPorts(ports ...string) Option

WithExposedPorts exposes ports without binding to host.

func WithHostname

func WithHostname(hostname string) Option

WithHostname sets the container hostname.

func WithImage

func WithImage(image string) Option

WithImage sets the container image.

func WithLabel

func WithLabel(key, value string) Option

WithLabel adds a container label.

func WithLabels

func WithLabels(labels map[string]string) Option

WithLabels sets multiple container labels.

func WithName

func WithName(name string) Option

WithName sets the container name.

func WithNetwork

func WithNetwork(network string) Option

WithNetwork attaches the container to a network.

func WithNetworkMode

func WithNetworkMode(mode string) Option

WithNetworkMode sets the network mode (bridge, host, none, container:<name>).

func WithNetworks

func WithNetworks(networks ...string) Option

WithNetworks attaches the container to multiple networks.

func WithOTelConfig

func WithOTelConfig(otelConfig *otel.Config) Option

WithOTelConfig enables OpenTelemetry instrumentation.

func WithPortBindings

func WithPortBindings(bindings map[string]string) Option

WithPortBindings sets detailed port bindings. Key: container port with protocol (e.g., "80/tcp") Value: host port (e.g., "8080")

func WithPorts

func WithPorts(portMapping string) Option

WithPorts adds port mappings in "containerPort:hostPort" format (e.g., "80:8080"). Protocol defaults to TCP. Use "80:8080/udp" for UDP.

func WithPrivileged

func WithPrivileged(privileged bool) Option

WithPrivileged runs the container in privileged mode.

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

func WithShmSize(size int64) Option

WithShmSize sets the size of /dev/shm in bytes.

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout sets the timeout for container operations.

func WithTmpfs

func WithTmpfs(path, options string) Option

WithTmpfs mounts a tmpfs filesystem.

func WithUser

func WithUser(user string) Option

WithUser sets the user.

func WithVolume

func WithVolume(hostPath, containerPath string) Option

WithVolume mounts a volume. Format: "hostPath:containerPath" or "hostPath:containerPath:ro"

func WithVolumeRO

func WithVolumeRO(hostPath, containerPath string) Option

WithVolumeRO mounts a read-only volume.

func WithVolumes

func WithVolumes(volumes map[string]string) Option

WithVolumes sets multiple volume mounts.

func WithWaitStrategy

func WithWaitStrategy(strategy WaitStrategy) Option

WithWaitStrategy sets the wait strategy for container readiness.

func WithWorkDir

func WithWorkDir(workDir string) Option

WithWorkDir sets the working directory.

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, cli *client.Client, containerID string) error
}

WaitStrategy defines how to wait for a container to be ready.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL