Documentation
¶
Overview ¶
Package docker provides a thin Docker Engine API client.
Supported endpoints:
- Unix socket: "/var/run/docker.sock" (Linux / macOS default)
- Named pipe: "npipe:////./pipe/docker_engine" (Windows default)
- TCP: "tcp://host:port" (DinD sidecars, all platforms)
This avoids pulling in the massive github.com/docker/docker SDK with its transitive dependencies (otel, protobuf, etc.). We only need a handful of API calls: create/start/stop/remove container, pull image, create/inspect network. The Docker Engine API is stable REST over a Unix socket or TCP.
Reference: https://docs.docker.com/engine/api/v1.45/
Index ¶
- Constants
- func DemuxStream(raw []byte) []byte
- func EndpointAliases(addresses ...string) []string
- func IsConflict(err error) bool
- func IsImageMissingErr(err error) bool
- func IsNotFound(err error) bool
- func ManagedLabels(service, resourceID string) map[string]string
- type Client
- func (d *Client) Available(timeout time.Duration) bool
- func (d *Client) ConnectNetwork(ctx context.Context, networkID, containerID string) error
- func (d *Client) ConnectNetworkWithAliases(ctx context.Context, networkID, containerID string, aliases []string) error
- func (d *Client) ConnectNetworkWithConfig(ctx context.Context, networkID, containerID string, cfg *EndpointSettings) error
- func (d *Client) ContainerLogs(ctx context.Context, id string, tail string) ([]byte, error)
- func (d *Client) ContainerLogsSince(ctx context.Context, id string, since time.Time) (io.ReadCloser, error)
- func (d *Client) ContainerLogsStream(ctx context.Context, id string, since time.Time) (io.ReadCloser, error)
- func (d *Client) ContainerMemoryUsage(ctx context.Context, id string) (usageBytes int64, err error)
- func (d *Client) ContainerStatsOneShot(ctx context.Context, id string) (ContainerStats, error)
- func (d *Client) CopyFileFromContainer(ctx context.Context, id, path string) ([]byte, error)
- func (d *Client) CopyToContainer(ctx context.Context, id, destPath string, tarData io.Reader) error
- func (d *Client) CreateContainer(ctx context.Context, name string, req *CreateContainerRequest) (string, error)
- func (d *Client) CreateNetwork(ctx context.Context, name string) (string, error)
- func (d *Client) CreateNetworkWithOptions(ctx context.Context, opts CreateNetworkOptions) (string, error)
- func (d *Client) CreateVolume(ctx context.Context, name string, labels map[string]string) error
- func (d *Client) DisconnectNetwork(ctx context.Context, networkID, containerID string) error
- func (d *Client) Exec(ctx context.Context, id string, cmd, env []string) (ExecResult, error)
- func (d *Client) GetContainerByName(ctx context.Context, name string) (*ContainerInspect, error)
- func (d *Client) ImageExists(ctx context.Context, image string) (bool, error)
- func (d *Client) ImageMatchesPlatform(ctx context.Context, image, platform string) (bool, error)
- func (d *Client) Info(ctx context.Context) (*SystemInfo, error)
- func (d *Client) InspectContainer(ctx context.Context, id string) (*ContainerInspect, error)
- func (d *Client) InspectNetwork(ctx context.Context, nameOrID string) (*NetworkInspect, error)
- func (d *Client) ListContainers(ctx context.Context, service string) ([]ContainerSummary, error)
- func (d *Client) ListNetworks(ctx context.Context, service string) ([]NetworkSummary, error)
- func (d *Client) ListVolumes(ctx context.Context, service string) ([]VolumeSummary, error)
- func (d *Client) Ping(ctx context.Context) error
- func (d *Client) PruneDanglingImages(ctx context.Context) error
- func (d *Client) PullImage(ctx context.Context, image string) error
- func (d *Client) PullImageForPlatform(ctx context.Context, image, platform string) error
- func (d *Client) RemoveContainer(ctx context.Context, id string, force bool) error
- func (d *Client) RemoveContainerForce(id string) error
- func (d *Client) RemoveNetwork(ctx context.Context, nameOrID string) error
- func (d *Client) RemoveVolume(ctx context.Context, name string, force bool) error
- func (d *Client) StartContainer(ctx context.Context, id string) error
- func (d *Client) StopContainer(ctx context.Context, id string, timeoutSec int) error
- func (d *Client) UpdateContainerResources(ctx context.Context, id string, update *UpdateResourcesRequest) error
- func (d *Client) WaitContainer(ctx context.Context, id string) (int, error)
- type ContainerConfig
- type ContainerInspect
- type ContainerNetwork
- type ContainerStats
- type ContainerSummary
- type CreateContainerRequest
- type CreateContainerResponse
- type CreateNetworkOptions
- type DaemonConnectedPayload
- type DemuxReader
- type EndpointIPAMConfig
- type EndpointSettings
- type ExecResult
- type GC
- func (g *GC) DrainAndSweep(ctx context.Context, service string)
- func (g *GC) ScheduleRemove(containerID string)
- func (g *GC) StartRemoveLoop(ctx context.Context)
- func (g *GC) StopAndScheduleRemove(containerID string)
- func (g *GC) StopNow(containerID string)
- func (g *GC) Sweep(service string)
- func (g *GC) SweepExcept(service string, keep func(resourceID string) bool)
- type HostConfig
- type ImageInspect
- type ImagePuller
- type Mount
- type MountVolumeOptions
- type NetworkIPAM
- type NetworkIPAMConfig
- type NetworkInspect
- type NetworkSummary
- type NetworkingConfig
- type PortBinding
- type ProbeResult
- type ServiceConfig
- type ServiceResult
- type Supervisor
- type SystemInfo
- type UpdateResourcesRequest
- type VolumeSummary
- type Watcher
Constants ¶
const ( // LabelManaged marks a resource as Overcast-managed. LabelManaged = "overcast.managed" // LabelService identifies which Overcast service owns the resource // (e.g. "lambda", "ecs", "rds", "ec2"). LabelService = "overcast.service" // LabelResourceID identifies the logical resource that owns the // Docker resource (e.g. function name, ECS task ID, VPC ID). LabelResourceID = "overcast.resource-id" )
Standard labels applied by Overcast services to Docker resources (containers and networks). The Docker watcher filters on LabelManaged so it only sees our resources.
Variables ¶
This section is empty.
Functions ¶
func DemuxStream ¶
DemuxStream returns the payload of a multiplexed Docker stream with the frame headers removed, or raw unchanged when raw is not framed at all.
The declared payload length is only ever trusted as far as the bytes that are actually present. Callers bound their reads — ContainerLogs stops at 64 KiB — so the final frame of a long log routinely arrives cut short, and those are the newest lines, the ones someone opened the log to read. A remainder too short to hold a header is the other half of that cut: binary junk rather than output, so it is dropped instead of printed.
A header that does not validate part-way through ends the walk and the rest is returned as it is. A stream that desynchronised is still the container's own bytes, and swallowing them would lose exactly what is being looked for.
Use DemuxReader instead when the source is an io.Reader — it does the same job without holding the whole stream in memory.
func EndpointAliases ¶
EndpointAliases returns unique, non-IP hostnames suitable for Docker DNS aliases.
func IsConflict ¶
IsConflict reports whether an error is a Docker 409 Conflict response (e.g. container name already in use).
func IsImageMissingErr ¶
IsImageMissingErr reports whether a container-create failure means the image is gone from the daemon ("No such image": removed after it was pulled or verified present).
func IsNotFound ¶
IsNotFound reports whether an error is a Docker 404 Not Found response.
Helpers in this package report a status two ways: doJSON and the by-name inspect build "…: 404: {body}", while every helper that drives doRequest itself builds "…: status 404". Matching only the first form meant this answered "no" for a 404 from StartContainer, StopContainer, ContainerLogs and the rest — so a start against a container Docker had already removed looked like an ordinary failure rather than the recoverable "it is gone, rebuild it" that it is.
func ManagedLabels ¶
ManagedLabels returns the standard Overcast labels for a Docker resource. All services should use this instead of constructing the map inline.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a lightweight Docker Engine API client.
func NewClient ¶
NewClient creates a Docker client for the given endpoint.
The endpoint can be:
- A Unix socket path: "/var/run/docker.sock" (Linux / macOS)
- A Windows named pipe: "npipe:////./pipe/docker_engine" (Windows)
- A TCP address: "tcp://host:port" (for DinD sidecars, all platforms)
Use the package-level defaultDockerSocket constant for the platform default.
func (*Client) ConnectNetwork ¶
ConnectNetwork attaches a container to a network.
func (*Client) ConnectNetworkWithAliases ¶
func (d *Client) ConnectNetworkWithAliases(ctx context.Context, networkID, containerID string, aliases []string) error
ConnectNetworkWithAliases attaches a container to a network with optional DNS aliases.
func (*Client) ConnectNetworkWithConfig ¶
func (d *Client) ConnectNetworkWithConfig(ctx context.Context, networkID, containerID string, cfg *EndpointSettings) error
ConnectNetworkWithConfig attaches a container to a network with an explicit endpoint configuration — aliases, a pinned address, or both.
func (*Client) ContainerLogs ¶
ContainerLogs fetches container stdout+stderr logs (non-streaming).
func (*Client) ContainerLogsSince ¶
func (d *Client) ContainerLogsSince(ctx context.Context, id string, since time.Time) (io.ReadCloser, error)
ContainerLogsSince fetches the full stdout+stderr log payload for a container starting from a given Unix timestamp (seconds). Used for reconciliation — after a streaming follower fails or on container teardown — to backfill any log frames that the streaming connection may have missed. Output includes per-line RFC3339Nano timestamps (timestamps=true) so the caller can deduplicate against events already delivered.
The response is a multiplexed Docker log stream identical in shape to ContainerLogsStream's body; wrap it in a DemuxReader to extract payload bytes.
func (*Client) ContainerLogsStream ¶
func (d *Client) ContainerLogsStream(ctx context.Context, id string, since time.Time) (io.ReadCloser, error)
ContainerLogsStream opens a streaming connection to the container log endpoint with follow=true. The caller is responsible for closing the returned ReadCloser. When ctx is cancelled the underlying HTTP connection is closed automatically, which causes reads on the stream to return an error, making the reader goroutine exit cleanly without an explicit close call.
The since parameter (Unix seconds with nanosecond fraction) lets a caller resume after a stream failure without re-receiving lines that were already delivered. Pass time.Time{} for "from start of container".
func (*Client) ContainerMemoryUsage ¶
ContainerMemoryUsage returns the current memory usage (in bytes) of a container.
func (*Client) ContainerStatsOneShot ¶
ContainerStatsOneShot returns one stats sample for a container.
one-shot=true matters: with stream=false alone the daemon waits an extra collection cycle (~1–2 s) to pre-fill the CPU delta fields, which both put seconds on any synchronous caller and overran short caller timeouts on slow (Docker-in-Docker) hosts — surfacing as memory "0" everywhere it was used. One-shot returns the current sample immediately; the precpu fields it leaves zeroed are ones this client never read anyway.
func (*Client) CopyFileFromContainer ¶
CopyFileFromContainer returns the raw bytes of a file path from inside a container using Docker's archive endpoint.
func (*Client) CopyToContainer ¶
CopyToContainer copies a tar archive into a container at the given path. This uses the Docker "Put Archive" API endpoint.
func (*Client) CreateContainer ¶
func (d *Client) CreateContainer(ctx context.Context, name string, req *CreateContainerRequest) (string, error)
CreateContainer creates a container (does not start it).
func (*Client) CreateNetwork ¶
CreateNetwork creates a Docker network. Returns the network ID. Ignores "already exists" errors.
func (*Client) CreateNetworkWithOptions ¶
func (d *Client) CreateNetworkWithOptions(ctx context.Context, opts CreateNetworkOptions) (string, error)
CreateNetworkWithOptions creates a Docker network with full control over labels, CIDR, and internal mode. Returns the network ID. Ignores "already exists" errors.
func (*Client) CreateVolume ¶
CreateVolume creates a named Docker volume. Creating an existing name is a no-op on the daemon side (Docker returns the existing volume), which makes this safe to call from reconciliation paths.
func (*Client) DisconnectNetwork ¶
DisconnectNetwork detaches a container from a network.
func (*Client) Exec ¶
Exec runs a command inside a running container and waits for it to finish. It returns the command's own exit status and output — a non-zero ExitCode is not an error here, because "the command ran and refused" is an answer the caller has to be able to tell apart from "the command never ran".
cmd is passed to the container verbatim, with no shell in between: quoting and word splitting never happen, so an argument containing spaces, quotes or a `$` reaches the process as one argument exactly as written. env adds to the container's own environment for this command only.
The exec is created with a TTY, which is what `docker exec -t` does. It costs the ability to tell stdout from stderr — neither of which a caller of this method distinguishes — and buys a plain byte stream instead of Docker's 8-byte-framed multiplexed one.
func (*Client) GetContainerByName ¶
GetContainerByName looks up a container by its name (without the leading "/"). Returns (nil, nil) if no container with that name exists.
func (*Client) ImageExists ¶
ImageExists checks if an image exists locally.
func (*Client) ImageMatchesPlatform ¶
ImageMatchesPlatform reports whether the local image tag exists and matches the requested Docker platform. Empty platform preserves ImageExists behavior.
func (*Client) Info ¶
func (d *Client) Info(ctx context.Context) (*SystemInfo, error)
Info returns the daemon's system information (GET /info).
func (*Client) InspectContainer ¶
InspectContainer returns container details.
func (*Client) InspectNetwork ¶
InspectNetwork returns network details.
func (*Client) ListContainers ¶
ListContainers returns all containers (running and stopped) that carry overcast.managed=true and optionally overcast.service=<service>. Pass an empty service string to list across all services.
func (*Client) ListNetworks ¶
ListNetworks returns all Overcast-managed networks, optionally filtered by service.
func (*Client) ListVolumes ¶
ListVolumes returns managed volumes, optionally filtered to one service.
func (*Client) PruneDanglingImages ¶
PruneDanglingImages removes all dangling (untagged) images. Equivalent to `docker image prune -f`.
Do NOT call this after a pull. "Dangling" means untagged, and an image pulled by digest ("repo@sha256:…") is untagged by definition, so a prune deletes the image the pull just fetched — the pull reports success and the container create that follows fails with "No such image". This used to run after every pull and made EFS's digest-pinned NFS export image unusable on any daemon with the classic image store (Docker Desktop's containerd store does not report digest-referenced images as dangling, so it only ever failed in CI).
The blast radius is wider than that one case: the filter is daemon-wide, so it also deletes the *user's* untagged images, which Overcast does not own, and it can race any service that has pulled an image but not yet created its container. Reclaiming disk is not worth either. Call it explicitly, if ever, and never on a path that is about to use an image.
func (*Client) PullImageForPlatform ¶
PullImageForPlatform pulls an image for a specific Docker platform such as linux/amd64. Docker Engine expects platform in the images/create query string, not in a JSON body.
func (*Client) RemoveContainer ¶
RemoveContainer removes a container. force=true kills it first if running.
func (*Client) RemoveContainerForce ¶
RemoveContainerForce removes a container using a background context with a deadline, ensuring cleanup always succeeds even when the request context is cancelled. Use this for teardown/cleanup paths only.
func (*Client) RemoveNetwork ¶
RemoveNetwork removes a Docker network by name or ID.
func (*Client) RemoveVolume ¶
RemoveVolume removes a named Docker volume. A missing volume is not an error, mirroring RemoveContainer's cleanup-friendly semantics.
func (*Client) StartContainer ¶
StartContainer starts a previously created container.
func (*Client) StopContainer ¶
StopContainer stops a running container with a timeout.
func (*Client) UpdateContainerResources ¶
func (d *Client) UpdateContainerResources(ctx context.Context, id string, update *UpdateResourcesRequest) error
UpdateContainerResources updates resource limits on a running container. Only the non-zero fields in the request are applied; zero values are ignored by the Docker daemon. Mirrors POST /containers/{id}/update.
type ContainerConfig ¶
type ContainerConfig struct {
Image string `json:"Image"`
Env []string `json:"Env,omitempty"`
Cmd []string `json:"Cmd,omitempty"`
Entrypoint []string `json:"Entrypoint,omitempty"`
WorkingDir string `json:"WorkingDir,omitempty"`
User string `json:"User,omitempty"`
ExposedPorts map[string]struct{} `json:"ExposedPorts,omitempty"`
Labels map[string]string `json:"Labels,omitempty"`
}
ContainerConfig describes the container's runtime configuration.
type ContainerInspect ¶
type ContainerInspect struct {
ID string `json:"Id"`
Name string `json:"Name"` // e.g. "/overcast-rds-mydb"
Labels map[string]string `json:"Labels"`
Config struct {
Labels map[string]string `json:"Labels"`
} `json:"Config"`
State struct {
Status string `json:"Status"` // "created", "running", "exited", etc.
Running bool `json:"Running"`
ExitCode int `json:"ExitCode"`
Error string `json:"Error"` // runtime error, e.g. "OCI runtime create failed: ..."
OOMKilled bool `json:"OOMKilled"` // true if the kernel OOM-killer terminated the container
StartedAt string `json:"StartedAt"`
FinishedAt string `json:"FinishedAt"`
} `json:"State"`
HostConfig struct {
Binds []string `json:"Binds"`
} `json:"HostConfig"`
NetworkSettings struct {
// Networks is keyed by network *name*; ContainerNetwork.NetworkID is
// the only way to match an entry against a network known by ID.
Networks map[string]ContainerNetwork `json:"Networks"`
// Ports maps "containerPort/proto" → list of host bindings.
// e.g. "3306/tcp" → [{"HostIp":"0.0.0.0","HostPort":"33060"}]
Ports map[string][]PortBinding `json:"Ports"`
} `json:"NetworkSettings"`
}
ContainerInspect holds container state and networking details.
func (*ContainerInspect) HasOvercastLabels ¶
func (c *ContainerInspect) HasOvercastLabels(service, resourceID string) bool
HasOvercastLabels reports whether the container was created by Overcast with the given service name and resource ID. Use this before reusing a container found by name to avoid accidentally attaching to a user-created container that happens to share the same name.
type ContainerNetwork ¶
type ContainerNetwork struct {
NetworkID string `json:"NetworkID"`
IPAddress string `json:"IPAddress"`
}
ContainerNetwork is one entry of a container's NetworkSettings.Networks.
type ContainerStats ¶
type ContainerStats struct {
// MemoryUsageBytes is cgroup usage minus inactive file cache — the same
// number `docker stats` shows, and closer to what a process actually
// occupies than the raw cgroup counter, which grows with page cache.
MemoryUsageBytes int64
// CPUTotalUsage is cumulative container CPU time in nanoseconds.
CPUTotalUsage uint64
// SystemCPUUsage is cumulative host CPU time in nanoseconds.
SystemCPUUsage uint64
// OnlineCPUs is the number of CPUs available to the container.
OnlineCPUs int
}
ContainerStats is a single point-in-time resource sample for a container. CPU counters are cumulative; a rate needs two samples (see the docker CLI formula: Δcpu_total / Δsystem_cpu × online_cpus × 100).
type ContainerSummary ¶
type ContainerSummary struct {
ID string `json:"Id"`
Names []string `json:"Names"` // e.g. ["/overcast-rds-mydb"]
Image string `json:"Image"`
State string `json:"State"` // "running", "exited", "created", etc.
Status string `json:"Status"` // human-readable, e.g. "Up 2 hours"
Labels map[string]string `json:"Labels"`
Ports []struct {
HostPort int `json:"PublicPort"`
ContainerPort int `json:"PrivatePort"`
Type string `json:"Type"`
} `json:"Ports"`
}
ContainerSummary is the lightweight container representation returned by GET /containers/json (list endpoint), as opposed to the full ContainerInspect returned by GET /containers/{id}/json.
func (*ContainerSummary) FirstName ¶
func (c *ContainerSummary) FirstName() string
FirstName returns the primary container name without the leading slash.
func (*ContainerSummary) ResourceID ¶
func (c *ContainerSummary) ResourceID() string
ResourceID returns the overcast.resource-id label value (empty string if not set).
func (*ContainerSummary) Service ¶
func (c *ContainerSummary) Service() string
Service returns the overcast.service label value (empty string if not set).
type CreateContainerRequest ¶
type CreateContainerRequest struct {
*ContainerConfig
HostConfig *HostConfig `json:"HostConfig,omitempty"`
NetworkingConfig *NetworkingConfig `json:"NetworkingConfig,omitempty"`
Platform string `json:"-"`
}
CreateContainerRequest combines all container creation parameters.
type CreateContainerResponse ¶
type CreateContainerResponse struct {
ID string `json:"Id"`
Warnings []string `json:"Warnings,omitempty"`
}
CreateContainerResponse is the response from container creation.
type CreateNetworkOptions ¶
type CreateNetworkOptions struct {
Name string
Labels map[string]string // nil = no labels
Subnet string // CIDR, e.g. "10.0.0.0/16"; empty = Docker default
Internal bool // true = no outbound internet access
}
CreateNetworkOptions configures a Docker network.
type DaemonConnectedPayload ¶
type DaemonConnectedPayload struct {
Client *Client `json:"-"`
Reconnected bool `json:"reconnected"`
}
DaemonConnectedPayload identifies the Docker client whose event stream has connected. A process can watch multiple daemon sockets, so services compare this pointer with the client they were wired to before reconciling.
type DemuxReader ¶
type DemuxReader struct {
// contains filtered or unexported fields
}
DemuxReader strips Docker's frame headers from a multiplexed stream as it is read, so a bufio.Scanner or bufio.Reader on top of it sees the container's own bytes and can assemble lines across frame boundaries — which it has to, because one line is not one frame: a JSON record from a structured logger routinely spans several.
Reads are served from the caller's own buffer and the type allocates nothing per Read; every line of every Lambda invocation goes through here.
An unframed (TTY) stream is recognised at the first header-sized read and copied through verbatim from then on.
func NewDemuxReader ¶
func NewDemuxReader(r io.Reader) *DemuxReader
NewDemuxReader wraps r, which may be a framed stream or a TTY one.
type EndpointIPAMConfig ¶
type EndpointIPAMConfig struct {
IPv4Address string `json:"IPv4Address,omitempty"`
}
EndpointIPAMConfig requests a specific address on a network. Docker rejects the connect outright when the address is outside the network's subnet or already taken, so callers that cannot guarantee either must be prepared to retry without it.
type EndpointSettings ¶
type EndpointSettings struct {
// Empty settings are enough to attach to a network. Aliases are advertised by
// Docker's embedded DNS to containers on the same user-defined network.
Aliases []string `json:"Aliases,omitempty"`
// IPAMConfig pins the address the container gets on this network. Left nil,
// Docker's own IPAM picks one.
IPAMConfig *EndpointIPAMConfig `json:"IPAMConfig,omitempty"`
}
EndpointSettings describes a container's attachment to a Docker network.
type ExecResult ¶
type ExecResult struct {
// ExitCode is the command's exit status. Zero means it succeeded.
ExitCode int
// Output is stdout and stderr interleaved, as a terminal would show them,
// bounded by maxExecOutputBytes. It is what a caller quotes when the
// command failed, so it is the command's own explanation and not a
// paraphrase of it.
Output string
}
ExecResult reports what a command run inside a container did.
type GC ¶
type GC struct {
// contains filtered or unexported fields
}
GC manages async Docker container cleanup. Services schedule containers for removal and the GC handles stop+remove in background goroutines:
- StopNow: fires immediately in a dedicated goroutine (non-blocking). A running container can still execute code — stop it ASAP.
- ScheduleRemove: enqueued and processed at leisure by the background loop. Failures are re-enqueued for retry (up to 3 attempts).
DrainAndSweep is called at shutdown: it drains the remove queue and then removes every managed container (Docker-level sweep), catching any orphans.
Zero value is invalid — use NewGC.
func NewGC ¶
NewGC creates a GC tied to a Docker client. keepContainers=true means containers are never removed — stop only (useful for debugging / post-mortem inspection).
func (*GC) DrainAndSweep ¶
DrainAndSweep shuts down the GC and removes every managed container for the given service. service="" matches all services. Blocks until complete or ctx expires during the drain phase.
Call from each service's Stop() method — this is the safety net that catches any container whose store record was already deleted but whose Docker container was never cleaned up.
Once DrainAndSweep returns the GC is inert; further StopNow / ScheduleRemove calls are no-ops.
func (*GC) ScheduleRemove ¶
ScheduleRemove enqueues a container for async removal. The background loop picks it up when it can — removal is not urgent once the container is stopped. Non-blocking. If the remove queue is full the request is dropped (logged).
func (*GC) StartRemoveLoop ¶
StartRemoveLoop begins the background remove worker. It blocks until ctx is cancelled or the GC is shut down. Safe to call multiple times — each call starts an independent worker goroutine tracked by the internal WaitGroup.
func (*GC) StopAndScheduleRemove ¶
StopAndScheduleRemove stops a container immediately (to halt any code running inside) and then queues it for deferred removal with exponential backoff. The stop fires in a dedicated goroutine so the caller can proceed without waiting for Docker to respond. The deferred removal retries indefinitely until the GC shuts down or the container is gone.
func (*GC) StopNow ¶
StopNow fires an async StopContainer in its own goroutine and returns immediately. Call from a delete handler before returning the response. Failures are logged at debug level — the remove loop will force-remove the container regardless of stop state.
func (*GC) Sweep ¶
Sweep removes every managed container for the given service without closing the GC. Call at startup to clean up orphaned containers from prior runs. service="" matches all services. Non-blocking — runs in a goroutine.
func (*GC) SweepExcept ¶
SweepExcept is Sweep with a veto. keep is asked about each candidate's resource ID and reports whether something live still owns it; those are left alone. Pass nil to sweep every non-running container, which is Sweep.
A stopped container is not automatically litter. Compute that is recreated on demand — an ECS task, a Lambda runtime — has no attachment to the container it last ran in, so state alone is a fair test there. A database does: a stopped RDS DB instance is a resource the user still owns and expects to start again, and its container has to outlive an Overcast restart. Sweeping it left the instance record pointing at an ID Docker no longer had, after which every start failed, every log fetch 404'd, and the instance still claimed to be available.
type HostConfig ¶
type HostConfig struct {
Binds []string `json:"Binds,omitempty"`
NetworkMode string `json:"NetworkMode,omitempty"`
Memory int64 `json:"Memory,omitempty"` // bytes
MemorySwap int64 `json:"MemorySwap,omitempty"` // bytes (-1 = unlimited)
NanoCPUs int64 `json:"NanoCPUs,omitempty"` // 1e9 = 1 CPU
AutoRemove bool `json:"AutoRemove,omitempty"`
PortBindings map[string][]PortBinding `json:"PortBindings,omitempty"`
Privileged bool `json:"Privileged,omitempty"` // required by k3s
Tmpfs map[string]string `json:"Tmpfs,omitempty"` // tmpfs mounts (path → options)
// CapAdd grants individual Linux capabilities on top of Docker's default
// set, named without the "CAP_" prefix (e.g. "DAC_READ_SEARCH"). Prefer it
// to Privileged: one capability is auditable, --privileged is not.
CapAdd []string `json:"CapAdd,omitempty"`
// ExtraHosts are "hostname:target" entries written into the container's
// /etc/hosts, where target is an IP or Docker's "host-gateway". /etc/hosts
// wins over DNS in glibc and musl, so an entry here shadows a public record
// for the same name inside this container only.
ExtraHosts []string `json:"ExtraHosts,omitempty"`
// Dns sets the container's resolvers. Docker keeps its own embedded
// resolver (127.0.0.11) in front and uses these as its upstream, so
// container-name service discovery is unaffected — but names these servers
// claim are answered by them, including wildcard subdomains that ExtraHosts
// cannot express. See internal/dns.
Dns []string `json:"Dns,omitempty"`
// Mounts is the structured alternative to Binds. Required for named-volume
// mounts that need VolumeOptions (e.g. Subpath); plain binds can stay in
// Binds — Docker merges both.
Mounts []Mount `json:"Mounts,omitempty"`
}
HostConfig describes the host-side container configuration.
type ImageInspect ¶
ImageInspect holds the platform metadata returned by Docker image inspect.
type ImagePuller ¶
type ImagePuller struct {
// contains filtered or unexported fields
}
ImagePuller deduplicates Docker image pulls. It ensures each image is pulled at most once per process lifetime. Services that run containers (RDS, ECS, Lambda) should share an ImagePuller rather than duplicating the sync.Map + sync.Once pattern.
func NewImagePuller ¶
func NewImagePuller(c *Client) *ImagePuller
NewImagePuller creates a puller backed by the given Docker client.
func (*ImagePuller) CreateContainerWithRetry ¶
func (p *ImagePuller) CreateContainerWithRetry(ctx context.Context, name string, req *CreateContainerRequest) (string, error)
CreateContainerWithRetry creates a container and, when the daemon reports its image missing — removed behind our back after the pull was recorded — forgets the stale pull record, re-pulls, and retries the create once. Without this, `docker rmi` of a task or DB image makes every later launch fail until restart, because the spent pull record short-circuits Ensure.
func (*ImagePuller) Ensure ¶
func (p *ImagePuller) Ensure(ctx context.Context, image string) error
Ensure pulls image if it hasn't been pulled yet. Concurrent calls for the same image block until the first pull completes and share its result. A FAILED pull drops the entry, so the next launch attempt retries instead of serving the cached error until restart — pulls are driven by user actions (RunTask, StartDBInstance, Invoke), not loops, so this cannot hammer a registry; what it prevents is one transient network failure bricking an image for the process lifetime.
func (*ImagePuller) Invalidate ¶
func (p *ImagePuller) Invalidate(image string)
Invalidate forgets that image was pulled, so the next Ensure pulls again. Call when the daemon proves the cached knowledge wrong — a create failing with "No such image" after the image was removed behind our back.
func (*ImagePuller) Prewarm ¶
func (p *ImagePuller) Prewarm(image string)
Prewarm starts Ensure in a background goroutine using a detached context so the pull is not tied to any caller's request deadline. Safe to call from request handlers at resource-creation time (CreateFunction, RegisterTaskDefinition, CreateDBInstance). If the same image is requested again on the invoke path, the caller blocks on the same sync.Once and reuses the in-flight pull.
type Mount ¶
type Mount struct {
// Type is "volume", "bind", or "tmpfs"; Overcast uses "volume".
Type string `json:"Type"`
Source string `json:"Source"` // volume name (for Type "volume")
Target string `json:"Target"` // absolute path inside the container
ReadOnly bool `json:"ReadOnly,omitempty"`
// VolumeOptions apply when Type is "volume".
VolumeOptions *MountVolumeOptions `json:"VolumeOptions,omitempty"`
}
Mount is one entry of HostConfig.Mounts (Engine API mounts specification).
type MountVolumeOptions ¶
type MountVolumeOptions struct {
// Subpath mounts only the named subdirectory of the volume (Engine API
// v1.45+). The subdirectory must already exist in the volume — the daemon
// rejects the mount otherwise.
Subpath string `json:"Subpath,omitempty"`
}
MountVolumeOptions holds volume-mount-specific options.
type NetworkIPAM ¶
type NetworkIPAM struct {
Config []NetworkIPAMConfig `json:"Config"`
}
NetworkIPAM describes IP address management for a Docker network.
type NetworkIPAMConfig ¶
NetworkIPAMConfig describes one IPAM pool.
type NetworkInspect ¶
type NetworkInspect struct {
ID string `json:"Id"`
Name string `json:"Name"`
Internal bool `json:"Internal"`
Labels map[string]string `json:"Labels"`
IPAM NetworkIPAM `json:"IPAM"`
}
NetworkInspect holds Docker network details.
type NetworkSummary ¶
type NetworkSummary struct {
ID string `json:"Id"`
Name string `json:"Name"`
Labels map[string]string `json:"Labels"`
IPAM NetworkIPAM `json:"IPAM"`
}
NetworkSummary is a lightweight network representation used by ListNetworks.
func (*NetworkSummary) ResourceID ¶
func (n *NetworkSummary) ResourceID() string
ResourceID returns the overcast.resource-id label value (empty string if not set).
func (*NetworkSummary) Service ¶
func (n *NetworkSummary) Service() string
Service returns the overcast.service label value (empty string if not set).
func (*NetworkSummary) Subnet ¶
func (n *NetworkSummary) Subnet() string
Subnet returns the first IPAM subnet for the network, or empty if unset.
type NetworkingConfig ¶
type NetworkingConfig struct {
EndpointsConfig map[string]*EndpointSettings `json:"EndpointsConfig,omitempty"`
}
NetworkingConfig specifies the container's networking configuration.
type PortBinding ¶
type PortBinding struct {
HostIP string `json:"HostIp,omitempty"`
HostPort string `json:"HostPort,omitempty"`
}
PortBinding represents a host-to-container port mapping.
type ProbeResult ¶
ProbeResult is returned by Probe on success.
func Probe ¶
func Probe(socketPath, network string, logger *zap.Logger) (*ProbeResult, error)
Probe creates a Docker client, verifies connectivity with retries, and ensures the named network exists. This is the common bootstrap pattern shared by Lambda, ECS, and RDS.
Returns nil with a logged warning (not an error) when Docker is unreachable — callers degrade gracefully (metadata ops work, container ops return errors).
type ServiceConfig ¶
type ServiceConfig struct {
// Name is used for logging ("rds", "ecs", "lambda").
Name string
// Socket is the Docker daemon socket path (e.g. /var/run/docker.sock).
Socket string
// Network is the Docker network to create for this service.
Network string
}
ServiceConfig describes a single service's Docker requirements. The Supervisor uses this to probe the socket, create the network, and wire the Docker client into the service.
type ServiceResult ¶
ServiceResult is returned per-service after a successful probe.
type Supervisor ¶
type Supervisor struct {
// contains filtered or unexported fields
}
Supervisor centralises Docker lifecycle management for the entire process. It deduplicates probes (one per unique socket path), creates per-service networks, runs a single event watcher per Docker daemon, and provides startup reconciliation.
Usage:
sup := docker.NewSupervisor(bus, logger)
results := sup.Probe(ctx, []ServiceConfig{...})
// wire results into services
sup.Run(ctx) // blocks — starts watchers; returns when ctx is done
sup.Close() // called during shutdown
func NewSupervisor ¶
func NewSupervisor(bus *events.Bus, logger *zap.Logger) *Supervisor
NewSupervisor creates a Supervisor that will publish Docker container events on the provided bus.
func (*Supervisor) Close ¶
func (s *Supervisor) Close()
Close signals all background goroutines (including Probe blockers and watchers) to stop. Safe to call before, during, or after Run.
func (*Supervisor) Probe ¶
func (s *Supervisor) Probe(ctx context.Context, configs []ServiceConfig) []ServiceResult
Probe probes Docker for each ServiceConfig. Configs sharing the same socket path reuse a single client connection and share a single availability probe. Each config gets its own network created. Returns one ServiceResult per successful config. Configs that fail to probe are logged and skipped.
func (*Supervisor) Run ¶
func (s *Supervisor) Run(ctx context.Context)
Run starts one Watcher goroutine per unique Docker client. It blocks until ctx is cancelled or Close is called. Call this from a goroutine after Probe.
type SystemInfo ¶
type SystemInfo struct {
// NCPU is the number of logical CPUs available to the daemon.
NCPU int `json:"NCPU"`
// MemTotal is the total memory available to the daemon, in bytes.
MemTotal int64 `json:"MemTotal"`
}
SystemInfo is the subset of GET /info Overcast reads: the resources of the machine the daemon runs containers on. That machine is not necessarily the one the Overcast process runs on — with Docker Desktop it is the desktop VM, with a DinD sidecar or a tcp:// endpoint it is another host entirely — so sizing decisions about containers must come from here, never from runtime.NumCPU() or the process's own view of memory.
type UpdateResourcesRequest ¶
type UpdateResourcesRequest struct {
NanoCPUs int64 `json:"NanoCPUs,omitempty"` // 1e9 = 1 CPU
Memory int64 `json:"Memory,omitempty"` // bytes
MemorySwap int64 `json:"MemorySwap,omitempty"` // bytes (-1 = unlimited)
}
UpdateResourcesRequest contains the resource fields that can be changed on a running container via the Docker Engine API POST /containers/{id}/update.
type VolumeSummary ¶
VolumeSummary is one entry from GET /volumes.
func (*VolumeSummary) ResourceID ¶
func (v *VolumeSummary) ResourceID() string
ResourceID returns the owning resource ID from the managed labels.
func (*VolumeSummary) Service ¶
func (v *VolumeSummary) Service() string
Service returns the owning service name from the managed labels.
type Watcher ¶
type Watcher struct {
// contains filtered or unexported fields
}
Watcher listens to the Docker Engine events stream and publishes typed events on an events.Bus. Only Overcast-managed resources (those with the overcast.managed label) are tracked — both containers and networks.
Usage:
w := docker.NewWatcher(client, bus, logger) go w.Run(ctx) // blocks until ctx is cancelled
func NewWatcher ¶
NewWatcher creates a Watcher that translates Docker container and network events into bus events. Call Run to start watching.