currus

package module
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 1, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

README

Currus

CI codecov Go Reference Go Report Card License Slack

Currus is a Go package that provides a single, neutral API for running and managing containers. It does not care which engine is installed on the host. It detects whether Docker, Podman, or containerd is present and drives whatever it finds through each engine's client API, so it never shells out to a CLI. Write your container logic once against one interface; Currus adapts to whatever runs underneath.

go get gopherly.dev/currus

[!IMPORTANT] Requires Go 1.26 or later.

import "gopherly.dev/currus"

Why Currus

  • One interface for Docker, Podman, and containerd. Write the code once.
  • Auto-detection that pings each candidate before it trusts the socket. A stale socket file does not count as a live engine.
  • Optional features live behind capability interfaces, so a missing feature is a typed ok == false, not a surprise at runtime.
  • Errors are normalized into a small set of sentinels you can match with errors.Is.
  • Built for testing: an in-memory fake and a shared conformance suite ship with the package.
  • Native client calls only. No CLI subprocesses to install, parse, or trust.

How it works

flowchart TD
    caller["Caller code"] --> api["Engine interface plus capability interfaces"]
    api --> sel["New: auto-detect or WithEngine(kind)"]
    sel --> dockerDrv["Docker-API driver (moby client)"]
    sel --> ctrdDrv["containerd driver (containerd v2)"]
    dockerDrv --> dockerSock["Docker socket"]
    dockerDrv --> podmanSock["Podman socket (Docker-compatible API)"]
    ctrdDrv --> ctrdSock["containerd socket"]

    style caller fill:#dbeafe,stroke:#3b82f6,color:#1e3a5f
    style api fill:#fef3c7,stroke:#f59e0b,color:#78350f
    style sel fill:#fef3c7,stroke:#f59e0b,color:#78350f
    style dockerDrv fill:#d1fae5,stroke:#10b981,color:#064e3b
    style ctrdDrv fill:#d1fae5,stroke:#10b981,color:#064e3b
    style dockerSock fill:#ede9fe,stroke:#8b5cf6,color:#3b0764
    style podmanSock fill:#ede9fe,stroke:#8b5cf6,color:#3b0764
    style ctrdSock fill:#ede9fe,stroke:#8b5cf6,color:#3b0764

The Docker-API driver serves both Docker and Podman, because Podman speaks the Docker Engine API. The containerd driver speaks the containerd v2 client API and adapts containerd to the same neutral, Docker-like model.

Contents

  1. Quick start
  2. Auto-detection
  3. Explicit engine selection
  4. Remote and rootless engines
  5. Container lifecycle
  6. Capability interfaces
  7. Logging and tracing
  8. Error handling
  9. Testing
  10. Engine capability matrix
  11. Examples
  12. License
  13. Community
  14. Contributing

Quick start

ctx := context.Background()

// Zero-config: detects whatever engine is installed.
// MustNew panics if no engine is reachable, which is handy at startup.
// Use New when you want to handle the error yourself.
eng := currus.MustNew(ctx, currus.WithLogger(slog.Default()))
defer eng.Close()

if err := eng.PullImage(ctx, "docker.io/library/redis:7", currus.PullImageOpts{}); err != nil {
    log.Fatalf("pull: %v", err)
}

id, err := eng.CreateContainer(ctx, currus.ContainerSpec{
    Image: "docker.io/library/redis:7",
    Name:  "cache",
    Env:   []string{"REDIS_ARGS=--save 60 1"},
})
if err != nil {
    log.Fatalf("create: %v", err)
}

if err := eng.StartContainer(ctx, id); err != nil {
    log.Fatalf("start: %v", err)
}

[!WARNING] MustNew panics if no engine is reachable. Use New when you want to handle the error yourself.

Auto-detection

New probes endpoints in this order and returns the first one that answers a Ping:

  1. CONTAINER_ENGINE environment variable (docker, podman, or containerd)
  2. Docker socket (unix:///var/run/docker.sock)
  3. Podman rootless socket ($XDG_RUNTIME_DIR/podman/podman.sock)
  4. Podman rootful socket (unix:///run/podman/podman.sock)
  5. containerd socket (unix:///run/containerd/containerd.sock)

Each candidate is validated with Ping before it is returned. A stale socket file that no daemon is listening on does not count as a live engine.

Explicit engine selection

eng, err := currus.New(ctx, currus.WithEngine(currus.Podman))

Available EngineKind values: currus.Docker, currus.Podman, currus.Containerd.

Remote and rootless engines

Use WithEndpoint to point at a non-default socket or a remote daemon. The Endpoint type supports several URI schemes:

// Remote Docker over TCP with mutual TLS.
eng, err := currus.New(ctx,
    currus.WithEngine(currus.Docker),
    currus.WithEndpoint(currus.Endpoint{
        Host: "tcp://docker-host:2376",
        TLS: &currus.TLSConfig{
            CACert: caCertPEM,
            Cert:   certPEM,
            Key:    keyPEM,
        },
    }),
)

Supported schemes:

  • unix:///var/run/docker.sock for a local socket (the default)
  • tcp://host:2376 for a remote daemon over TCP (use TLSConfig for mutual TLS)
  • ssh://user@host for a remote Podman or Docker daemon over SSH
  • npipe:////./pipe/docker_engine for a Windows named pipe

For containerd, Endpoint.Host accepts either a raw socket path (/run/containerd/containerd.sock) or a unix:// URI; both forms work. Set Endpoint.Namespace to pick the namespace (defaults to default).

Rootless Docker and rootless Podman are picked up by auto-detection through the XDG_RUNTIME_DIR socket path, so they usually work with no extra configuration.

Container lifecycle

Every Engine supports the universal container lifecycle:

eng.PullImage(ctx, ref, currus.PullImageOpts{})
id, _ := eng.CreateContainer(ctx, currus.ContainerSpec{Image: "nginx:latest"})
eng.StartContainer(ctx, id)
eng.StopContainer(ctx, id, currus.StopContainerOpts{Timeout: 10 * time.Second})
eng.RemoveContainer(ctx, id, currus.RemoveContainerOpts{Force: true})
containers, _ := eng.ListContainers(ctx, currus.ListContainersOpts{All: true})

Capability interfaces

Not every engine supports every feature, so non-universal features live behind optional capability interfaces. You discover them at runtime with a type assertion. This lets you branch cleanly instead of assuming a feature is there:

// Logs: containerd has no native container logs.
if lg, ok := eng.(currus.Logger); ok {
    rc, _ := lg.ContainerLogs(ctx, id, currus.ContainerLogsOpts{Follow: false, Tail: 100})
    defer rc.Close()
    io.Copy(os.Stdout, rc)
}

// Exec
if ex, ok := eng.(currus.Execer); ok {
    ex.Exec(ctx, id, currus.ExecOpts{Cmd: []string{"redis-cli", "ping"}})
}

The full set of capability interfaces:

Interface What it does
Logger read container log streams
Execer run a command inside a container
Inspector read full container metadata
Stater read point-in-time CPU and memory usage
Waiter block until a container exits
Eventer subscribe to engine lifecycle events
Imager list, remove, and tag images
Networker create, list, and remove networks
Volumer create, list, and remove named volumes
Copier copy files into and out of a container

For traits that are not method-shaped, call eng.Capabilities(). It returns a Caps value with these fields:

  • RootlessCapable reports whether the driver runs rootless.
  • SupportsPods reports whether the engine groups containers into pods.
  • OneShotRun reports whether the engine can run a container in a single call.
  • NamespaceModel names the isolation model, for example "containerd".

Logging and tracing

Pass a *slog.Logger with WithLogger to see structured debug output for each operation. Pass an OpenTelemetry TracerProvider with WithTracerProvider to wrap each engine call in a span named currus.<method>:

eng, err := currus.New(ctx,
    currus.WithLogger(slog.Default()),
    currus.WithTracerProvider(tp),
)

Error handling

Currus normalizes engine errors into a small, stable set of sentinels you can match with errors.Is:

if err := eng.RemoveContainer(ctx, id, currus.RemoveContainerOpts{Force: true}); err != nil {
    if errors.Is(err, currus.ErrNotFound) {
        // already gone, which is fine
    } else {
        return fmt.Errorf("remove container: %w", err)
    }
}

Sentinel errors: ErrNotFound, ErrAlreadyExists, ErrConflict, ErrNotImplemented, ErrUnsupported, and ErrNoEngine (returned by New when no reachable engine is found).

Testing

Swap the real engine for an in-memory fake in your tests, so you need no daemon:

import "gopherly.dev/currus/currustest"

func TestStartsCache(t *testing.T) {
    eng := currustest.New() // *currustest.Fake: implements Engine and every capability interface
    // ... drive the same code path against the fake ...
}

The conformance package holds a shared behavioural test suite that checks any Engine against the neutral contract. It runs against the in-memory fake on every unit run, and against real daemons in the integration layer:

func TestConformance(t *testing.T) {
    conformance.Run(t, func(t *testing.T) currus.Engine {
        return currustest.New()
    })
}

Engine capability matrix

Yes means the engine implements the interface. No means a type assertion to that interface returns ok == false.

Capability Docker Podman containerd
Core lifecycle (Engine) Yes Yes Yes
Logs (Logger) Yes Yes No
Exec (Execer) Yes Yes No
Inspect (Inspector) Yes Yes No
Stats (Stater) Yes Yes No
Wait (Waiter) Yes Yes No
Events (Eventer) Yes Yes No
Images (Imager) Yes Yes No
Networks (Networker) Yes Yes No
Volumes (Volumer) Yes Yes No
Copy files (Copier) Yes Yes No

[!NOTE] The containerd driver implements only the core Engine today. containerd has no native container logs through its client API, and the other capabilities are not yet adapted to its model.

Examples

See the examples/ directory for complete runnable programs:

  • examples/basic covers auto-detect, pull, create, start, read logs, and clean up.

Run any example with:

go run ./examples/basic/...

License

Currus is released under the Apache License 2.0. See LICENSE.

Community

Join #gopherly on the Gophers Slack.

Contributing

nix develop          # enter the dev shell (auto-loaded via .envrc + direnv)
nix run .#lint       # run golangci-lint
nix run .#fmt        # auto-fix formatting
nix run .#test-unit  # run unit tests (no daemon required)

# run integration tests against a real engine:
CURRUS_TEST_ENGINE=docker nix run .#test-integration
CURRUS_TEST_ENGINE=podman nix run .#test-integration

Documentation

Overview

Package currus provides a single, neutral API for the container lifecycle that auto-detects and drives Docker, Podman, or containerd through engine client APIs, never by shelling out to a CLI.

Architecture

Currus follows the database/sql shape: a neutral interface on top, with pluggable engine drivers beneath. Engine selection is auto-detected by default (probing endpoints in priority order with a Ping validation), or explicit via WithEngine.

The neutral container model is explicitly Docker-like. Engines that do not natively implement it (notably containerd) are adapted to it. Where an engine cannot express part of the model, that part is surfaced via a capability interface and Caps rather than silently faked.

Quick start

Zero-config: detect whatever engine is installed.

eng := currus.MustNew(ctx, currus.WithLogger(slog.Default()))
defer eng.Close()

if err := eng.PullImage(ctx, "docker.io/library/redis:7", currus.PullImageOpts{}); err != nil {
    log.Fatal(err)
}

id, err := eng.CreateContainer(ctx, currus.ContainerSpec{
    Image: "docker.io/library/redis:7",
    Name:  "cache",
})
if err != nil {
    log.Fatal(err)
}

if err := eng.StartContainer(ctx, id); err != nil {
    log.Fatal(err)
}

Explicit engine selection:

eng, err := currus.New(ctx, currus.WithEngine(currus.Podman))

Remote Docker over TLS:

eng, err := currus.New(ctx,
    currus.WithEngine(currus.Docker),
    currus.WithEndpoint(currus.Endpoint{
        Host: "tcp://docker-host:2376",
        TLS: &currus.TLSConfig{
            CACert: caCertPEM,
            Cert:   certPEM,
            Key:    keyPEM,
        },
    }),
)

Engine interface

Engine exposes the small core that every backend supports: identity, Ping, Close, and the universal container lifecycle (pull/create/start/ stop/remove/list). Non-universal features live behind optional capability interfaces discovered by type assertion:

if lg, ok := eng.(currus.Logger); ok {
    rc, _ := lg.ContainerLogs(ctx, id, currus.ContainerLogsOpts{})
    defer rc.Close()
    io.Copy(os.Stdout, rc)
}

Network attachment

Containers can join one or more networks at creation time by populating [ContainerSpec.Networks]. The first network is attached before the container starts; additional networks are connected immediately after create. Both Docker and Podman honor this field; containerd ignores it.

id, err := eng.CreateContainer(ctx, currus.ContainerSpec{
    Image:    "ghcr.io/example/sidecar:latest",
    Name:     "kind-sidecar",
    Networks: []currus.NetworkAttachment{{Name: "kind"}},
})

The Networker capability also exposes [Networker.ConnectContainer] and [Networker.DisconnectContainer] for attaching and detaching after start.

Resolved endpoint

The EndpointReporter capability exposes the URI the engine actually connected to. This is useful when deriving the host socket path for a bind-mount into a sidecar container:

sock := "/var/run/docker.sock"
if er, ok := eng.(currus.EndpointReporter); ok {
    sock = strings.TrimPrefix(er.Endpoint().Host, "unix://")
}

Capability matrix

The following table shows which capabilities each engine implements. A "—" means the engine does not implement the interface; type-asserting it yields ok == false.

Capability       │ Docker │ Podman │ containerd
─────────────────┼────────┼────────┼───────────
Engine           │ ✓      │ ✓      │ ✓
Logger           │ ✓      │ ✓      │ —
Execer           │ ✓      │ ✓      │ —
Inspector        │ ✓      │ ✓      │ —
Stater           │ ✓      │ ✓      │ —
Waiter           │ ✓      │ ✓      │ —
Eventer          │ ✓      │ ✓      │ —
Imager           │ ✓      │ ✓      │ —
Networker        │ ✓      │ ✓      │ —
Volumer          │ ✓      │ ✓      │ —
Copier           │ ✓      │ ✓      │ —
EndpointReporter │ ✓      │ ✓      │ ✓

Error handling

Errors are normalized into a stable sentinel taxonomy (ErrNotFound, ErrAlreadyExists, ErrConflict, ErrNotImplemented, ErrUnsupported) usable with errors.Is / errors.As across every engine:

if errors.Is(err, currus.ErrNotFound) { ... }

Testing

Swap the real engine for the in-memory fake from gopherly.dev/currus/currustest:

eng := currustest.New() // implements Engine + all capability interfaces

The gopherly.dev/currus/conformance package provides a shared behavioral test suite that verifies any Engine implementation. Driver maintainers can run it against both the in-memory fake and real daemons.

Platform support

The Docker-API driver (serving Docker and Podman) is pure HTTP and builds on Linux, macOS, and Windows. The containerd driver is supported on Linux only (containerd is not available on macOS or Windows outside of a VM). Rootless Docker and rootless Podman are fully supported via auto-detection through the XDG_RUNTIME_DIR socket paths.

For more details, see https://pkg.go.dev/gopherly.dev/currus

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNotFound is returned when the requested resource does not exist.
	ErrNotFound = errors.New("not found")

	// ErrAlreadyExists is returned when a resource already exists and the
	// operation requires uniqueness.
	ErrAlreadyExists = errors.New("already exists")

	// ErrConflict is returned when an operation conflicts with the current
	// state of a resource.
	ErrConflict = errors.New("conflict")

	// ErrNotImplemented is returned by a driver that has not yet implemented
	// a particular method of a capability interface.
	ErrNotImplemented = errors.New("not implemented")

	// ErrUnsupported is returned when the underlying engine does not support
	// the requested operation at all.
	ErrUnsupported = errors.New("unsupported")

	// ErrNoEngine is returned by New when no reachable engine is found.
	ErrNoEngine = errors.New("no reachable container engine found")

	// ErrInvalidSpec is returned when a ContainerSpec or option field is
	// malformed or missing a required value (e.g. an empty Image field).
	ErrInvalidSpec = errors.New("invalid container spec")
)

Sentinel errors returned by all Engine implementations. Use errors.Is to compare; all errors may be wrapped.

Functions

This section is empty.

Types

type Caps

type Caps struct {
	RootlessCapable bool
	SupportsPods    bool
	OneShotRun      bool
	NamespaceModel  string
}

Caps holds informational, non-method-shaped engine traits.

Caps never mirrors a capability interface: method-shaped features (e.g. ContainerLogs) are discovered by type assertion against the capability interfaces (Logger, Execer, …). Caps holds only boolean or string descriptors of structural engine properties.

type ConnectOpts added in v0.2.0

type ConnectOpts struct {
	// Aliases are optional extra DNS names for this container on the network.
	Aliases []string
}

ConnectOpts controls ConnectContainer.

type Container

type Container struct {
	ID     ContainerID
	Name   string
	Image  string
	State  string
	Labels map[string]string
}

Container is a neutral representation of a running or stopped container.

type ContainerID

type ContainerID string

ContainerID is the opaque identifier for a container returned by CreateContainer. Using a named type prevents accidental mix-ups with image refs, volume IDs, and other string identifiers.

type ContainerInfo

type ContainerInfo struct {
	ID         ContainerID
	Name       string
	Image      string
	ImageID    string
	Labels     map[string]string
	State      ContainerState
	Command    []string
	Env        []string
	WorkingDir string
	Mounts     []Mount
}

ContainerInfo holds the full details of a container as returned by Inspect.

type ContainerLogsOpts

type ContainerLogsOpts struct {
	Follow bool
	Tail   int
}

ContainerLogsOpts controls what the ContainerLogs stream returns.

type ContainerSpec

type ContainerSpec struct {
	// Image is the container image reference (e.g. "docker.io/library/alpine:3.20").
	Image string
	// Name is an optional human-readable name assigned to the container.
	Name string
	// Command overrides the image ENTRYPOINT. Leave nil to use the image default.
	Command []string
	// Args are the arguments passed to Command, or to the image ENTRYPOINT when
	// Command is nil.
	Args []string
	// Env holds environment variables in KEY=VALUE form.
	Env []string
	// WorkingDir sets the working directory for the container process. Leave
	// empty to use the image default.
	WorkingDir string
	// Labels are arbitrary key/value metadata attached to the container.
	Labels map[string]string
	// Mounts lists filesystem mounts attached to the container.
	Mounts []Mount
	// Ports lists port bindings between the container and the host.
	Ports []Port
	// Resources constrains the CPU and memory available to the container.
	Resources Resources
	// Restart is the restart policy applied when the container exits.
	Restart RestartPolicy
	// Networks lists the networks to join at creation time, in order.
	Networks []NetworkAttachment
}

ContainerSpec is the desired state of a container.

It is built from the common core of OCI runtime spec, Docker container config, and Podman specgen. Growth is additive within the nested sub-structs so callers that set only a few fields are unaffected by new additions.

func (ContainerSpec) Validate added in v0.3.0

func (s ContainerSpec) Validate() error

Validate reports whether the spec is well-formed, returning a wrapped ErrInvalidSpec when a required field is missing. Drivers call it before create; callers may also use it to check a spec ahead of time.

type ContainerState

type ContainerState struct {
	Running    bool
	Paused     bool
	Restarting bool
	ExitCode   int
	Error      string
	StartedAt  time.Time
	FinishedAt time.Time
}

ContainerState holds the runtime state of an inspected container.

type ContainerStats

type ContainerStats struct {
	CPUPercent    float64
	MemoryUsage   uint64
	MemoryLimit   uint64
	NetworkInput  uint64
	NetworkOutput uint64
}

ContainerStats holds point-in-time resource usage statistics for a container.

type Copier

type Copier interface {
	// CopyToContainer copies a TAR-archived content stream into the container
	// at DestPath.
	CopyToContainer(ctx context.Context, id ContainerID, o CopyToContainerOpts) error

	// CopyFromContainer copies a path from the container's filesystem and
	// returns a TAR-archived stream of the content.
	CopyFromContainer(ctx context.Context, id ContainerID, o CopyFromContainerOpts) (io.ReadCloser, error)
}

Copier is the capability interface for copying files into and out of a container. Implemented by Docker and Podman; not available on containerd.

type CopyFromContainerOpts

type CopyFromContainerOpts struct {
	// SrcPath is the path inside the container to copy content from.
	SrcPath string
}

CopyFromContainerOpts controls CopyFromContainer.

type CopyToContainerOpts

type CopyToContainerOpts struct {
	// DestPath is the path inside the container to copy the content to.
	DestPath string
	// Content is a TAR archive reader to copy into the container.
	Content io.Reader
}

CopyToContainerOpts controls CopyToContainer.

type CreateNetworkOpts

type CreateNetworkOpts struct {
	Driver string
}

CreateNetworkOpts controls CreateNetwork.

type CreateVolumeOpts

type CreateVolumeOpts struct {
	Driver string
}

CreateVolumeOpts controls CreateVolume.

type DisconnectOpts added in v0.2.0

type DisconnectOpts struct {
	// Force disconnects the container even if it is running.
	Force bool
}

DisconnectOpts controls DisconnectContainer.

type Endpoint

type Endpoint struct {
	// Host is the endpoint URI (see above for supported schemes).
	Host string

	// Namespace is the containerd namespace to use. Ignored by Docker and
	// Podman drivers. Empty defaults to "default".
	Namespace string

	// TLS carries the TLS configuration for tcp:// connections. Nil means
	// no client-side TLS material (the server still uses TLS if configured).
	// Ignored by unix:// and ssh:// schemes.
	TLS *TLSConfig
}

Endpoint describes the connection details for an engine daemon.

The following URI schemes are supported:

  • unix:///var/run/docker.sock — local Docker socket (default)
  • tcp://host:2376 — remote Docker over TCP (use TLSConfig for mutual TLS)
  • ssh://user@host — remote Podman or Docker over SSH (system SSH agent)
  • npipe:////./pipe/docker_engine — Windows named pipe

For containerd, Host accepts either a raw socket path (/run/containerd/containerd.sock) or a unix:// URI; both forms work. The tcp://, ssh://, and npipe:// schemes are not supported by containerd.

type EndpointReporter added in v0.2.0

type EndpointReporter interface {
	Endpoint() Endpoint
}

EndpointReporter reports the endpoint the engine actually connected to. Callers can use the resolved Host URI to, for example, derive the host socket path when bind-mounting it into a sidecar container:

sock := "/var/run/docker.sock"
if er, ok := eng.(currus.EndpointReporter); ok {
    sock = strings.TrimPrefix(er.Endpoint().Host, "unix://")
}

Implemented by Docker, Podman, and containerd drivers.

type Engine

type Engine interface {
	Kind() EngineKind
	Capabilities() Caps
	Ping(ctx context.Context) error
	Close() error
	PullImage(ctx context.Context, ref string, o PullImageOpts) error
	CreateContainer(ctx context.Context, spec ContainerSpec) (ContainerID, error)
	StartContainer(ctx context.Context, id ContainerID) error
	StopContainer(ctx context.Context, id ContainerID, o StopContainerOpts) error
	RemoveContainer(ctx context.Context, id ContainerID, o RemoveContainerOpts) error
	ListContainers(ctx context.Context, o ListContainersOpts) ([]Container, error)
}

Engine is the small core interface every backend implements.

It carries only what every backend supports: identity, Ping, Close, and the universal container lifecycle (pull / create / start / stop / remove / list). Non-universal features live behind optional capability interfaces discovered by type assertion:

if lg, ok := eng.(currus.Logger); ok { ... }

The full set of optional capability interfaces is: Logger, Execer, Inspector, Stater, Waiter, Eventer, Imager, Networker, Volumer, Copier, EndpointReporter. See [doc.go] or the README capability matrix for per-engine support.

This keeps Engine from growing into a 50-method interface and makes missing features a typed ok==false rather than a runtime error.

func MustNew

func MustNew(ctx context.Context, opts ...Option) Engine

MustNew is like New but panics if no engine is available.

Intended for package-level initialization or program startup where an unavailable engine is a programmer error, not a recoverable condition. Do not use MustNew in package code that might be called from tests or in contexts where the environment is not controlled.

func New

func New(ctx context.Context, opts ...Option) (Engine, error)

New creates an Engine by detecting or constructing the appropriate backend.

With no options, New probes endpoints in priority order until a reachable engine responds to Ping:

  1. CONTAINER_ENGINE env var override
  2. Docker socket (unix:///var/run/docker.sock)
  3. Podman rootless socket (~/.local/share/containers/podman/machine/ podman.sock or $XDG_RUNTIME_DIR/podman/podman.sock)
  4. Podman rootful socket (unix:///run/podman/podman.sock)
  5. containerd socket (unix:///run/containerd/containerd.sock)

Use WithEngine to skip detection and select a backend explicitly. Use WithEndpoint to override the default socket path.

type EngineKind

type EngineKind string

EngineKind is the typed selector for a specific container engine backend.

const (
	// Docker selects the Docker-API driver backed by the moby client.
	Docker EngineKind = "docker"

	// Podman selects the Docker-API driver pointed at the Podman socket.
	// Podman speaks the Docker Engine API; the same moby driver is used.
	Podman EngineKind = "podman"

	// Containerd selects the containerd v2 client driver.
	Containerd EngineKind = "containerd"
)

type Event

type Event struct {
	Type   string
	Action string
	Actor  string
}

Event is a neutral representation of an engine lifecycle event.

type Eventer

type Eventer interface {
	Events(ctx context.Context) (<-chan Event, error)
}

Eventer is the capability interface for subscribing to engine events. Implemented by Docker and Podman; not available on containerd.

type ExecOpts

type ExecOpts struct {
	Cmd          []string
	Env          []string
	WorkingDir   string
	AttachStdout bool
	AttachStderr bool
}

ExecOpts configures an Exec call.

type ExecResult

type ExecResult struct {
	ExitCode int
	Stdout   io.Reader
	Stderr   io.Reader
}

ExecResult holds the outcome of an Exec call.

type Execer

type Execer interface {
	Exec(ctx context.Context, id ContainerID, o ExecOpts) (ExecResult, error)
}

Execer is the capability interface for running commands inside a container.

type Image

type Image struct {
	ID        string
	Tags      []string
	SizeBytes int64
}

Image is a neutral representation of an image present in the engine.

type Imager

type Imager interface {
	ListImages(ctx context.Context, o ListImagesOpts) ([]Image, error)
	RemoveImage(ctx context.Context, ref string, o RemoveImageOpts) error
	TagImage(ctx context.Context, src, dst string) error
}

Imager is the capability interface for image management beyond PullImage.

type Inspector

type Inspector interface {
	Inspect(ctx context.Context, id ContainerID) (ContainerInfo, error)
}

Inspector is the capability interface for reading full container metadata. Implemented by Docker and Podman; not available on containerd.

Usage:

if ins, ok := eng.(currus.Inspector); ok {
    info, _ := ins.Inspect(ctx, id)
}

type ListContainersOpts

type ListContainersOpts struct {
	All bool
}

ListContainersOpts filters the ListContainers result.

type ListImagesOpts

type ListImagesOpts struct {
	All bool
}

ListImagesOpts filters the ListImages result.

type ListNetworksOpts

type ListNetworksOpts struct{}

ListNetworksOpts filters the ListNetworks result.

type ListVolumesOpts

type ListVolumesOpts struct{}

ListVolumesOpts filters the ListVolumes result.

type Logger

type Logger interface {
	ContainerLogs(ctx context.Context, id ContainerID, o ContainerLogsOpts) (io.ReadCloser, error)
}

Logger is the capability interface for reading container log streams.

This is a capability — not part of the core Engine — because containerd has no native container logs. Callers must check the assertion:

if lg, ok := eng.(currus.Logger); ok {
    rc, _ := lg.ContainerLogs(ctx, id, currus.ContainerLogsOpts{})
    defer rc.Close()
    io.Copy(os.Stdout, rc)
}

The returned io.ReadCloser always produces plain, demultiplexed output. Implementations are responsible for stripping any transport framing (e.g. the Docker 8-byte stream headers) before returning the reader.

type Mount

type Mount struct {
	// Type identifies the kind of mount (bind, volume, or tmpfs).
	Type MountType
	// Source is the host path for bind mounts or the volume name for named volumes.
	Source string
	// Target is the absolute path inside the container where the mount is attached.
	Target string
	// ReadOnly makes the mount read-only when true.
	ReadOnly bool
}

Mount describes a single filesystem mount attached to a container.

type MountType

type MountType string

MountType identifies the kind of filesystem mount.

const (
	// MountTypeBind is a bind mount from a host path.
	MountTypeBind MountType = "bind"
	// MountTypeVolume is a named engine-managed volume.
	MountTypeVolume MountType = "volume"
	// MountTypeTmpfs is an in-memory tmpfs mount.
	MountTypeTmpfs MountType = "tmpfs"
)

type Network

type Network struct {
	ID     NetworkID
	Name   string
	Driver string
}

Network is a neutral representation of a container network.

type NetworkAttachment added in v0.2.0

type NetworkAttachment struct {
	// Name is the network name or ID to join (e.g. "kind").
	Name string
	// Aliases are optional extra DNS names for this container on the network.
	Aliases []string
}

NetworkAttachment describes a single network a container should join.

type NetworkID

type NetworkID string

NetworkID is the opaque identifier for a container network.

type Networker

type Networker interface {
	CreateNetwork(ctx context.Context, name string, o CreateNetworkOpts) (NetworkID, error)
	ListNetworks(ctx context.Context, o ListNetworksOpts) ([]Network, error)
	RemoveNetwork(ctx context.Context, id NetworkID, o RemoveNetworkOpts) error
	// ConnectContainer connects a running container to a network.
	ConnectContainer(ctx context.Context, net NetworkID, id ContainerID, o ConnectOpts) error
	// DisconnectContainer disconnects a container from a network.
	DisconnectContainer(ctx context.Context, net NetworkID, id ContainerID, o DisconnectOpts) error
}

Networker is the capability interface for managing container networks. Implemented by Docker and Podman; not available on containerd.

type Option

type Option func(*engineConfig)

Option is a functional option for New and MustNew.

func WithEndpoint

func WithEndpoint(ep Endpoint) Option

WithEndpoint sets the connection endpoint for the engine daemon. See Endpoint for supported URI schemes.

func WithEngine

func WithEngine(kind EngineKind) Option

WithEngine selects a specific engine backend instead of auto-detecting. Pass currus.Docker, currus.Podman, or currus.Containerd.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger attaches a *slog.Logger to the engine.

func WithTracerProvider

func WithTracerProvider(tp trace.TracerProvider) Option

WithTracerProvider attaches an OpenTelemetry TracerProvider. Each engine operation is wrapped in a span named "currus.<method>".

type Port

type Port struct {
	// Container is the port number exposed by the container.
	Container uint16
	// Host is the host port to bind to. Zero lets the engine pick a free port.
	Host uint16
	// Protocol is the transport protocol ("tcp" or "udp"). Defaults to "tcp"
	// when empty.
	Protocol string
}

Port describes a single port binding between container and host.

type PullImageOpts

type PullImageOpts struct {
	Platform string
}

PullImageOpts controls the PullImage operation.

type RemoveContainerOpts

type RemoveContainerOpts struct {
	Force         bool
	RemoveVolumes bool
}

RemoveContainerOpts controls the RemoveContainer operation.

type RemoveImageOpts

type RemoveImageOpts struct {
	Force bool
}

RemoveImageOpts controls the RemoveImage behavior.

type RemoveNetworkOpts

type RemoveNetworkOpts struct {
	Force bool
}

RemoveNetworkOpts controls RemoveNetwork.

type RemoveVolumeOpts

type RemoveVolumeOpts struct {
	Force bool
}

RemoveVolumeOpts controls RemoveVolume.

type Resources

type Resources struct {
	// NanoCPUs is the CPU limit expressed in units of 1e-9 CPUs (e.g. 500_000_000
	// for half a CPU). Zero means no limit.
	NanoCPUs int64
	// MemoryBytes is the memory limit in bytes. Zero means no limit.
	MemoryBytes int64
}

Resources constrains the CPU and memory available to a container.

type RestartMode added in v0.3.0

type RestartMode string

RestartMode describes the restart strategy for a container.

const (
	// RestartNever disables automatic restarts. This is the default.
	RestartNever RestartMode = ""

	// RestartAlways restarts the container regardless of exit status.
	RestartAlways RestartMode = "always"

	// RestartOnFailure restarts the container only when it exits with a
	// non-zero exit code. Use MaxRetries to cap the number of attempts.
	RestartOnFailure RestartMode = "on-failure"

	// RestartUnlessStopped restarts the container unless it was explicitly
	// stopped by the user.
	RestartUnlessStopped RestartMode = "unless-stopped"
)

type RestartPolicy

type RestartPolicy struct {
	// Mode is the restart strategy. Empty (RestartNever) means no restart.
	Mode RestartMode
	// MaxRetries is the maximum number of restart attempts. Only meaningful
	// with RestartOnFailure; ignored otherwise.
	MaxRetries int
}

RestartPolicy describes when and how many times the engine should restart a container after it exits.

type Stater

type Stater interface {
	Stats(ctx context.Context, id ContainerID, o StatsOpts) (ContainerStats, error)
}

Stater is the capability interface for reading point-in-time resource usage. Implemented by Docker and Podman; not available on containerd.

type StatsOpts

type StatsOpts struct{}

StatsOpts controls the Stats call (reserved for future extension).

type StopContainerOpts

type StopContainerOpts struct {
	Timeout time.Duration
}

StopContainerOpts controls the StopContainer operation.

type TLSConfig

type TLSConfig struct {
	CACert             []byte
	Cert               []byte
	Key                []byte
	InsecureSkipVerify bool
}

TLSConfig carries the TLS material needed to connect to a remote engine.

type Volume

type Volume struct {
	ID         VolumeID
	Driver     string
	Mountpoint string
}

Volume is a neutral representation of a named volume.

type VolumeID

type VolumeID string

VolumeID is the opaque identifier for a named volume.

type Volumer

type Volumer interface {
	CreateVolume(ctx context.Context, name string, o CreateVolumeOpts) (VolumeID, error)
	ListVolumes(ctx context.Context, o ListVolumesOpts) ([]Volume, error)
	RemoveVolume(ctx context.Context, id VolumeID, o RemoveVolumeOpts) error
}

Volumer is the capability interface for managing named volumes. Implemented by Docker and Podman; not available on containerd.

type WaitCondition added in v0.3.0

type WaitCondition string

WaitCondition describes the event to wait for in a WaitContainer call.

const (
	// WaitConditionNotRunning waits until the container is no longer running.
	// This is the default when Condition is empty.
	WaitConditionNotRunning WaitCondition = "not-running"

	// WaitConditionNextExit waits for the container's next exit event.
	WaitConditionNextExit WaitCondition = "next-exit"

	// WaitConditionRemoved waits until the container has been removed.
	WaitConditionRemoved WaitCondition = "removed"
)

type WaitContainerOpts

type WaitContainerOpts struct {
	// Condition is the event to wait for. Empty defaults to WaitConditionNotRunning.
	Condition WaitCondition
}

WaitContainerOpts controls WaitContainer.

type WaitResult

type WaitResult struct {
	StatusCode int
	Error      string
}

WaitResult is the outcome of a WaitContainer call.

type Waiter

type Waiter interface {
	WaitContainer(ctx context.Context, id ContainerID, o WaitContainerOpts) (<-chan WaitResult, error)
}

Waiter is the capability interface for blocking until a container exits. Implemented by Docker and Podman; not available on containerd.

Directories

Path Synopsis
Package conformance provides the shared behavioral test suite that verifies any currus.Engine implementation against the neutral contract defined by the currus package.
Package conformance provides the shared behavioral test suite that verifies any currus.Engine implementation against the neutral contract defined by the currus package.
Package currustest provides an in-memory fake currus.Engine for use in tests.
Package currustest provides an in-memory fake currus.Engine for use in tests.
examples
basic command
Command basic demonstrates the core container lifecycle: auto-detect the engine, pull an image, create and start a container, read its logs, then clean up.
Command basic demonstrates the core container lifecycle: auto-detect the engine, pull an image, create and start a container, read its logs, then clean up.

Jump to

Keyboard shortcuts

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