Documentation
¶
Overview ¶
Package docker wraps the Docker Engine API. Every call goes through the SDK's HTTP-over-socket client, never a shelled-out `docker` CLI command, per the project's rules for node communication and orchestration. This file defines the narrow interface reconcile controllers depend on, so tests can fake it without a daemon.
Index ¶
- Constants
- Variables
- func CPUPercent(current, previous CPUStatsRaw) float64
- func CapReadCloser(rc io.ReadCloser, limit int64) io.ReadCloser
- func CapReader(r io.Reader, limit int64) io.Reader
- func ImageDigestOf(ref string) string
- func IsPoolExhausted(err error) bool
- func NetworkShareDriverOpts(opts NetworkShareVolumeOpts) (map[string]string, error)
- func NormalizeArch(a string) string
- func OwnerKindLabelKey(instanceKey string) string
- func OwnerNameLabelKey(instanceKey string) string
- func PinImageRef(ref, digest string) string
- func RegistryHost(ref string) string
- func SiblingLabelKey(instanceKey, suffix string) string
- func UnpinImageRef(ref string) string
- type ArchiveRuntime
- type BindMount
- type Browser
- type BrowserSpec
- type CPUStatsRaw
- type Client
- func (c *Client) BridgeGatewayIP(ctx context.Context) (string, error)
- func (c *Client) CDIDevices(ctx context.Context) ([]string, error)
- func (c *Client) Close() error
- func (c *Client) CopyFromContainer(ctx context.Context, containerID, path string, maxBytes int64) (io.ReadCloser, PathStat, error)
- func (c *Client) CopyToContainer(ctx context.Context, containerID, dir string, content io.Reader, ...) error
- func (c *Client) Create(ctx context.Context, spec ContainerSpec) (string, error)
- func (c *Client) DaemonSecurity(ctx context.Context) (DaemonSecurity, error)
- func (c *Client) DescribeNetworks(ctx context.Context, prefix string) ([]NetworkDetail, error)
- func (c *Client) DiskUsage(ctx context.Context) (DiskUsage, error)
- func (c *Client) DockerRootDir(ctx context.Context) (string, error)
- func (c *Client) EnsureImageID(ctx context.Context, ref string) (id string, pulled bool, err error)
- func (c *Client) EnsureNetwork(ctx context.Context, name string) (string, error)
- func (c *Client) EnsureNetworkVolume(ctx context.Context, name string, opts NetworkShareVolumeOpts) error
- func (c *Client) EnsureVolume(ctx context.Context, name string) error
- func (c *Client) Events(ctx context.Context) (<-chan Event, <-chan error)
- func (c *Client) Exec(ctx context.Context, containerID string, cmd []string) (io.ReadCloser, error)
- func (c *Client) ExecTTY(ctx context.Context, containerID string, opts ExecTTYOptions) (ExecSession, error)
- func (c *Client) ExecWithInput(ctx context.Context, containerID string, cmd []string, stdin io.Reader) (io.ReadCloser, error)
- func (c *Client) ImageInUse(ctx context.Context, imageID string) (bool, error)
- func (c *Client) InspectByName(ctx context.Context, name string) (*ContainerState, error)
- func (c *Client) InspectExitState(ctx context.Context, name string) (*ExitState, error)
- func (c *Client) InspectImageDetails(ctx context.Context, ref string) (*ImageDetails, error)
- func (c *Client) InspectImageID(ctx context.Context, ref string) (string, error)
- func (c *Client) InspectImageLayers(ctx context.Context, ref string) ([]string, error)
- func (c *Client) InspectLocalContainer(ctx context.Context, name string) (LocalContainer, error)
- func (c *Client) ListByPrefix(ctx context.Context, prefix string) ([]ContainerState, error)
- func (c *Client) ListContainersByLabel(ctx context.Context, label string) ([]LabeledContainer, error)
- func (c *Client) ListImages(ctx context.Context, repo string) ([]ImageInfo, error)
- func (c *Client) ListLocalContainers(ctx context.Context) ([]LocalContainer, error)
- func (c *Client) ListNamedVolumes(ctx context.Context) ([]NamedVolume, error)
- func (c *Client) ListNetworksByPrefix(ctx context.Context, prefix string) ([]NetworkInfo, error)
- func (c *Client) ListVolumesByPrefix(ctx context.Context, prefix string) ([]NamedVolume, error)
- func (c *Client) LoadImage(ctx context.Context, r io.Reader) error
- func (c *Client) Logs(ctx context.Context, containerID string, follow bool, since time.Time) (<-chan LogLine, <-chan error)
- func (c *Client) NetworkConnect(ctx context.Context, name, containerID string) error
- func (c *Client) NetworkDisconnect(ctx context.Context, name, containerID string, force bool) error
- func (c *Client) NetworkPoolUsage(ctx context.Context) (PoolUsage, error)
- func (c *Client) NodePlatform(ctx context.Context) (NodePlatform, error)
- func (c *Client) PauseContainer(ctx context.Context, id string) error
- func (c *Client) Ping(ctx context.Context) error
- func (c *Client) Prune(ctx context.Context, keep []string) PruneResult
- func (c *Client) PruneAnonymousVolumes(ctx context.Context) (PruneVolumesResult, error)
- func (c *Client) PruneBuildCache(ctx context.Context) (PruneBuildCacheResult, error)
- func (c *Client) PruneContainers(ctx context.Context, keep []string) (PruneContainersResult, error)
- func (c *Client) PruneDanglingImages(ctx context.Context) (PruneImagesResult, error)
- func (c *Client) Remove(ctx context.Context, id string, force bool) error
- func (c *Client) RemoveImageByID(ctx context.Context, imageID string) error
- func (c *Client) RemoveNetwork(ctx context.Context, name string) error
- func (c *Client) RemoveNetworkIfEmpty(ctx context.Context, name string) (bool, error)
- func (c *Client) RemoveVolume(ctx context.Context, name string) error
- func (c *Client) ResolveImage(ctx context.Context, ref string, auth *RegistryAuth, requireFresh bool) (ResolvedImage, error)
- func (c *Client) RunOneShot(ctx context.Context, spec OneShotSpec) (OneShotResult, error)
- func (c *Client) RunToCompletion(ctx context.Context, spec ContainerSpec, maxOutput int) (RunOutcome, error)
- func (c *Client) Runtime() RuntimeInfo
- func (c *Client) RuntimeNames(ctx context.Context) ([]string, error)
- func (c *Client) ServerVersion(ctx context.Context) (string, error)
- func (c *Client) Start(ctx context.Context, id string) error
- func (c *Client) StartBrowser(ctx context.Context, spec BrowserSpec) (*Browser, error)
- func (c *Client) Stats(ctx context.Context, containerID string) (ContainerStats, error)
- func (c *Client) Stop(ctx context.Context, id string, timeout time.Duration) error
- func (c *Client) TagImage(ctx context.Context, source, target string) error
- func (c *Client) TestRegistryAuth(ctx context.Context, host, username, password string) error
- func (c *Client) UnpauseContainer(ctx context.Context, id string) error
- func (c *Client) UntagImage(ctx context.Context, ref string) error
- func (c *Client) UpdateResources(ctx context.Context, id string, resources Resources) error
- func (c *Client) WatchContainer(ctx context.Context, name string) (<-chan LifecycleEvent, <-chan error)
- type ClientOption
- type ContainerMount
- type ContainerNetwork
- type ContainerSpec
- type ContainerState
- type ContainerStats
- type CreateDeclaration
- type CreateDeclarer
- type DaemonSecurity
- type DiskUsage
- type Event
- type EventAction
- type ExecExitError
- type ExecSession
- type ExecTTYOptions
- type ExitState
- type ExitStateInspector
- type GPURequest
- type HardeningConfig
- type HardeningMode
- type HardeningReport
- type ImageDetailInspector
- type ImageDetails
- type ImageInfo
- type ImageInspector
- type ImageLayerInspector
- type ImageLoader
- type ImagePinner
- type ImageResolver
- type ImageSource
- type LabeledContainer
- type LifecycleEvent
- type LifecycleWatcher
- type LocalContainer
- type LocalNetwork
- type LogLine
- type NamedVolume
- type NetworkAdmin
- type NetworkAttachment
- type NetworkDetail
- type NetworkInfo
- type NetworkPool
- type NetworkShareVolumeOpts
- type NodePlatform
- type OneShotResult
- type OneShotSpec
- type PathStat
- type PoolExhaustedError
- type PoolUsage
- type PortBinding
- type PortMapping
- type PruneBuildCacheResult
- type PruneContainersResult
- type PruneImagesResult
- type PruneResult
- type PruneVolumesResult
- type PullConfig
- type PullEvent
- type PullPhase
- type PullTracker
- type RegistryAuth
- type ResolvedImage
- type Resources
- type RunOutcome
- type RunToCompletionRuntime
- type Runtime
- type RuntimeInfo
- type RuntimeKind
- type ServiceUser
- type StatsInspector
- type TTYRuntime
- type TTYSize
- type VolumeMount
Constants ¶
const ( // EnvNetworkPool overrides the block per-app networks are carved from. EnvNetworkPool = "APP_NETWORK_POOL" // DefaultNetworkPool is 1024 /24 networks, in a private range Docker's // own defaults (172.17/16 to 172.31/16, 192.168/16) never touch. DefaultNetworkPool = "10.88.0.0/14" )
const ( )
Network share protocols NetworkShareDriverOpts understands. Mirrors store.NetworkShareProtocolNFS/CIFS; this package never imports internal/store, so the caller translates.
const ( LayerQueued = "queued" LayerDownloading = "downloading" LayerDownloaded = "downloaded" LayerExtracting = "extracting" LayerComplete = "complete" LayerExists = "already exists" )
Layer states derived from the daemon's pull stream.
const ( EnvPullInterval = "APP_DEPLOY_TRACE_PULL_INTERVAL" EnvPullMinPercent = "APP_DEPLOY_TRACE_PULL_MIN_PERCENT" EnvPullMaxLayerLine = "APP_DEPLOY_TRACE_PULL_MAX_LAYER_LINES" )
Environment variables behind PullConfig.
const ( // DefaultPidsLimit is generous enough for forking servers and database // backends while still stopping a fork bomb from exhausting the host. DefaultPidsLimit int64 = 4096 )
const EnvGPUAttach = "APP_GPU_ATTACH"
EnvGPUAttach selects how GPUs are attached to containers: "auto" (default) prefers CDI when Docker lists the NVIDIA CDI devices and falls back to the legacy nvidia runtime, "legacy" never uses CDI.
const PullStatusCached = "cached"
PullStatusCached is PullEvent.Status when the image was already local.
Variables ¶
var ( ErrArchivePathNotFound = errors.New("docker: path not found in container") ErrArchiveTooLarge = errors.New("docker: archive stream exceeds the size limit") ErrArchiveUnsupported = errors.New("docker: this node's container runtime does not support archive copy") )
Archive copy errors callers can match.
var MinimalCapabilities = []string{
"CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL",
"NET_BIND_SERVICE", "SETGID", "SETUID",
}
MinimalCapabilities is what ordinary images need to start after a drop-all: entrypoints that chown data dirs and drop privileges (postgres, mysql, redis via gosu/su-exec: CHOWN, FOWNER, FSETID, DAC_OVERRIDE, SETUID, SETGID), nginx/node/python binding ports below 1024 (NET_BIND_SERVICE), and init scripts signalling other users' processes (KILL). Docker's default set also grants NET_RAW, MKNOD, SETFCAP, SETPCAP, SYS_CHROOT and AUDIT_WRITE, deliberately left out.
Functions ¶
func CPUPercent ¶ added in v0.2.0
func CPUPercent(current, previous CPUStatsRaw) float64
CPUPercent is cpuPercent for a caller keeping its own previous sample (internal/telemetry.Collector) instead of a one-shot response's own, sometimes-empty PreCPUStats.
func CapReadCloser ¶
func CapReadCloser(rc io.ReadCloser, limit int64) io.ReadCloser
CapReadCloser is CapReader for a stream that also needs closing.
func CapReader ¶
CapReader fails with ErrArchiveTooLarge once more than limit bytes are read. limit <= 0 returns r unchanged.
func ImageDigestOf ¶ added in v0.2.0
ImageDigestOf returns the digest a pinned reference carries, or "".
func IsPoolExhausted ¶
IsPoolExhausted reports whether err is a daemon address-pool exhaustion.
func NetworkShareDriverOpts ¶ added in v0.2.0
func NetworkShareDriverOpts(opts NetworkShareVolumeOpts) (map[string]string, error)
NetworkShareDriverOpts translates opts into the driver options Docker's own `local` volume driver expects for its built-in NFS/CIFS passthrough (the same options `docker volume create --driver local --opt type=nfs ...` would pass on the CLI), so EnsureNetworkVolume never needs to shell out: the Engine API's volume-create endpoint accepts these directly.
func NormalizeArch ¶
NormalizeArch maps the daemon's uname style architecture to the OCI name an image reports.
func OwnerKindLabelKey ¶
OwnerKindLabelKey and OwnerNameLabelKey are the owner label keys.
func OwnerNameLabelKey ¶
OwnerNameLabelKey is the label key holding the owning resource's name.
func PinImageRef ¶ added in v0.2.0
PinImageRef appends digest to ref unless ref already carries one.
func RegistryHost ¶
RegistryHost returns the registry domain a reference pulls from.
func SiblingLabelKey ¶
SiblingLabelKey derives a network label key next to the instance label key, so owner labels share the reserved prefix without importing spec.
func UnpinImageRef ¶ added in v0.2.0
UnpinImageRef strips a trailing "@digest" so a pinned reference can be re-resolved (deploy --pull) or shown as its tag.
Types ¶
type ArchiveRuntime ¶
type ArchiveRuntime interface {
// CopyFromContainer streams the tar of path (absolute, inside the container).
CopyFromContainer(ctx context.Context, containerID, path string, maxBytes int64) (io.ReadCloser, PathStat, error)
// CopyToContainer extracts the tar (optionally gzip) in content under dir.
CopyToContainer(ctx context.Context, containerID, dir string, content io.Reader, maxBytes int64) error
}
ArchiveRuntime is the tar copy half of a runtime, kept off Runtime so the narrow test fakes need no stub. maxBytes caps the tar stream (0 means no cap); exceeding it fails the stream with ErrArchiveTooLarge.
type BindMount ¶ added in v0.2.0
BindMount attaches a real host directory to a path inside a container, HostPath a real filesystem path on whichever node the container runs on, not a Docker volume name (VolumeMount's own doc comment above). internal/reconcile/application is the only caller today, translating store.ServiceBindMount into this at container- create time; internal/api gates persisting a non-empty BindMounts to AbilityRoot callers and rejects the deny-listed host paths internal/compose's own validateBindMountHostPath defines, both upstream of this package, which trusts HostPath the same way it already trusts VolumeMount.Name.
type Browser ¶ added in v0.2.0
type Browser struct {
ID string
// Addr is the host loopback address the debug port is published on.
Addr string
// contains filtered or unexported fields
}
Browser is a running one-shot browser container.
type BrowserSpec ¶ added in v0.2.0
type BrowserSpec struct {
Name string
Image string
Command []string
Env map[string]string
Labels map[string]string
// Network is the only network the container joins; empty means none.
Network string
User string
MemoryBytes int64
NanoCPUs int64
PidsLimit int64
ShmBytes int64
// Tmpfs maps a container path to its tmpfs mount options.
Tmpfs map[string]string
// DebugPort is the container port published to 127.0.0.1 on a random host port.
DebugPort int
}
BrowserSpec describes a short-lived, locked-down container that exposes one debugging port on the host loopback interface.
type CPUStatsRaw ¶ added in v0.2.0
CPUStatsRaw is the raw counters CPUPercent needs to compute a delta-based percentage across two of a caller's own samples.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the real Runtime implementation, talking to the Docker Engine API over the local socket. It never shells out to the `docker` CLI, per the project's Docker Engine API only rule.
func NewClient ¶
func NewClient(opts ...ClientOption) (*Client, error)
NewClient builds a Client from the standard Docker environment (DOCKER_HOST, DOCKER_CERT_PATH, etc.), negotiating API version against whatever daemon is actually running rather than pinning one. APP_CONTAINER_RUNTIME_SOCKET, when set, takes precedence over DOCKER_HOST for the connection itself (DetectRuntimeSocket applies the same precedence when only reporting, not connecting).
func (*Client) BridgeGatewayIP ¶
BridgeGatewayIP returns the gateway IP of Docker's default "bridge" network: the address from which a container attached to that network (the default whenever Create's NetworkingConfig is nil, which is every container this Client creates) can reach the host. Used by cmd/levelrail/mesh.go's containerDNSAddr to compute a container-reachable address for the mesh DNS server, since no WireGuard mesh IP is bound to anything yet.
func (*Client) CDIDevices ¶ added in v0.2.0
CDIDevices lists the NVIDIA CDI devices the Docker daemon has discovered (for example "nvidia.com/gpu=0" and "nvidia.com/gpu=all"). The daemon lists none when CDI is unsupported or no spec is installed.
func (*Client) CopyFromContainer ¶
func (c *Client) CopyFromContainer(ctx context.Context, containerID, path string, maxBytes int64) (io.ReadCloser, PathStat, error)
CopyFromContainer implements ArchiveRuntime over GET /containers/{id}/archive.
func (*Client) CopyToContainer ¶
func (c *Client) CopyToContainer(ctx context.Context, containerID, dir string, content io.Reader, maxBytes int64) error
CopyToContainer implements ArchiveRuntime over PUT /containers/{id}/archive.
func (*Client) DaemonSecurity ¶ added in v0.2.0
func (c *Client) DaemonSecurity(ctx context.Context) (DaemonSecurity, error)
DaemonSecurity reads the daemon's SecurityOptions from /info.
func (*Client) DescribeNetworks ¶
DescribeNetworks implements NetworkAdmin. It inspects each match because the list endpoint does not reliably report attached containers.
func (*Client) DiskUsage ¶
DiskUsage reports Docker's own storage accounting via GET /system/df, aggregated into the four categories the General settings page shows. See the DiskUsage type's own doc comment for why this is a different number from the control plane's own DataDir usage.
func (*Client) DockerRootDir ¶ added in v0.2.0
DockerRootDir returns the daemon's storage root, where named volumes live.
func (*Client) EnsureImageID ¶ added in v0.2.0
EnsureImageID makes ref available locally and returns its image ID. pulled reports whether this call had to pull it.
func (*Client) EnsureNetwork ¶
EnsureNetwork implements Runtime.
func (*Client) EnsureNetworkVolume ¶ added in v0.2.0
func (c *Client) EnsureNetworkVolume(ctx context.Context, name string, opts NetworkShareVolumeOpts) error
EnsureNetworkVolume creates a `local`-driver Docker volume backed by an NFS or CIFS network share, the same idempotent-by-name contract EnsureVolume documents for a plain local volume: Docker's VolumeCreate returns the existing volume, not an error, when name already exists.
func (*Client) EnsureVolume ¶
EnsureVolume implements Runtime. Docker's VolumeCreate is itself idempotent by name (creating a volume that already exists returns the existing volume, not an error), so this is a thin wrapper, not a check-then-create with its own race window.
func (*Client) Exec ¶
Exec implements Runtime. It creates an exec configuration on containerID, attaches to it to actually run cmd, and returns a ReadCloser streaming cmd's stdout back to the caller.
Docker's exec attach stream, with no tty (never requested here), is always multiplexed per pkg/stdcopy's frame format even though this call only asks for one logical output: stdout is copied straight through to the pipe this method returns, stderr is captured separately into a capped buffer purely so a non-zero exit has something useful to explain itself with. ContainerExecAttach itself has no notion of exit status, only ContainerExecInspect does, so exit code is checked only after the stream ends, in the background goroutine that drives the copy.
func (*Client) ExecTTY ¶ added in v0.2.0
func (c *Client) ExecTTY(ctx context.Context, containerID string, opts ExecTTYOptions) (ExecSession, error)
ExecTTY implements TTYRuntime. Unlike Exec, the attach stream is raw rather than stdcopy-multiplexed: Docker merges stdout and stderr onto the PTY exactly as a real terminal does, so there is nothing to demultiplex and the bytes pass straight through in both directions.
func (*Client) ExecWithInput ¶
func (c *Client) ExecWithInput(ctx context.Context, containerID string, cmd []string, stdin io.Reader) (io.ReadCloser, error)
ExecWithInput implements Runtime. It is execCreateAttach plus a stdin side: everything Exec does (see Exec's own doc comment for the stdout/stderr/exit-code handling, unchanged here, both methods share streamExecOutput) plus a second goroutine that copies stdin into the exec session's write side and then closes it, the same "close the write half once the reader is exhausted" signal a real pipe or terminal gives a process reading until EOF.
The stdin-copy goroutine and streamExecOutput's own stdout-copy goroutine run concurrently, not sequentially: a command that starts producing output before this process has finished writing every byte of stdin (true of psql on a large dump, which echoes NOTICE/COPY progress lines as it goes) would otherwise deadlock, stdin blocked on a full kernel pipe buffer nobody is draining because this method hadn't gotten to the read side yet.
func (*Client) ImageInUse ¶ added in v0.2.0
ImageInUse reports whether any container, in any state, was created from imageID.
func (*Client) InspectByName ¶
InspectByName implements Runtime. Scoped to this Client's own instance label when WithInstanceLabel configured one (every real caller looks up an exact, platform-derived name this same instance's own desired state produced, e.g. application.ContainerName's result, so this never hides a container a legitimate caller actually wants): a name-only match on a Docker daemon shared by more than one control-plane instance could otherwise resolve to another instance's container by the same name and start operating on it (exec, live resource updates, restart-in-place), see internal/spec.InstanceLabelKey's own doc comment.
func (*Client) InspectExitState ¶ added in v0.2.0
InspectExitState implements ExitStateInspector. Unlike InspectByName, this calls the real container-inspect endpoint (ContainerList's Summary shape carries no OOMKilled/ExitCode field at all), so it costs one extra round trip a caller should only pay for when it actually needs to tell "still starting" apart from "already exited or OOM-killed."
func (*Client) InspectImageDetails ¶
InspectImageDetails implements ImageDetailInspector.
func (*Client) InspectImageID ¶ added in v0.2.0
InspectImageID implements ImageInspector.
func (*Client) InspectImageLayers ¶ added in v0.2.0
InspectImageLayers implements ImageLayerInspector.
func (*Client) InspectLocalContainer ¶ added in v0.2.0
InspectLocalContainer returns name with its full launch configuration: entrypoint plus command, mounts, extra hosts and published ports. Used to read a foreign reverse proxy's configuration, never to manage it.
func (*Client) ListByPrefix ¶
ListByPrefix implements Runtime. Deliberately NOT scoped to this Client's own instance label, unlike InspectByName: GET /api/v1/system/containers (internal/api's handleListContainers) calls this with an empty prefix specifically to show every container on the box "whether or not Levelrail manages it" (that handler's own doc comment), a legitimate use this method must not break. Every ownership-sensitive caller (internal/reconcile/application's staleContainers) instead reads the returned ContainerState.Labels itself and applies its own instance check on top of the name-prefix match, the same "list broadly, filter narrowly at the call site" split NetworkInfo.Labels/NetworkCleanupController use for networks.
func (*Client) ListContainersByLabel ¶ added in v0.2.0
func (c *Client) ListContainersByLabel(ctx context.Context, label string) ([]LabeledContainer, error)
ListContainersByLabel lists containers in any state carrying label (key=value), scoped to this Client's instance label when configured.
func (*Client) ListImages ¶
ListImages implements Runtime.
func (*Client) ListLocalContainers ¶ added in v0.2.0
func (c *Client) ListLocalContainers(ctx context.Context) ([]LocalContainer, error)
ListLocalContainers returns every container on the daemon, running or not.
func (*Client) ListNamedVolumes ¶ added in v0.2.0
func (c *Client) ListNamedVolumes(ctx context.Context) ([]NamedVolume, error)
ListNamedVolumes returns every Docker volume on this daemon that matches this project's own named-volume convention (isProjectNamedVolume) and, when this Client was built with WithInstanceLabel, carries this exact instance's own label. An unlabeled volume, or one labeled with a different instance's ID, is never returned: the same exact-match-only discipline application.NetworkCleanupController.ownNetworks was tightened to (see that method's own doc comment), applied here from the start rather than needing a later fix. EnsureVolume already stamps every volume this Client creates with the instance label when one is configured, so there is no pre-labeling-upgrade case to accommodate the way the network cleanup fix had to: an exact match is always achievable for a volume this instance genuinely created.
No instance label configured (single-instance mode, the default) is a no-op on this check: every project-named volume is a candidate, mirroring PruneAnonymousVolumes' own "empty means no scoping" fallback.
func (*Client) ListNetworksByPrefix ¶
ListNetworksByPrefix implements Runtime. Deliberately NOT scoped to this Client's own instance label, same reasoning ListByPrefix's own doc comment gives for containers: this returns every matching network regardless of who created it, and NetworkCleanupController (the one caller today) applies its own instance check against the returned NetworkInfo.Labels before treating anything as orphaned.
func (*Client) ListVolumesByPrefix ¶ added in v0.2.0
ListVolumesByPrefix returns this instance's volumes whose name starts with prefix, with on-disk sizes from the daemon's disk usage report (SizeBytes is -1 when the driver reports none).
func (*Client) LoadImage ¶ added in v0.2.0
LoadImage implements ImageLoader through the Engine API's image load endpoint. r is read to the end but not closed.
func (*Client) Logs ¶
func (c *Client) Logs(ctx context.Context, containerID string, follow bool, since time.Time) (<-chan LogLine, <-chan error)
Logs streams a container's log output, demultiplexed into a channel of individually stream-tagged, timestamped lines. Reads directly from the Docker Engine API's log endpoint, per the observability design rule that logs never come from the json-file driver's on-disk files, matching Events' own channel-based streaming shape rather than a blocking read loop.
Docker multiplexes stdout and stderr into a single byte stream with an 8-byte frame header per chunk whenever a container wasn't created with a TTY, which is every container this codebase creates (ContainerSpec has no TTY field, so container.Config.Tty is always Docker's zero value, false). stdcopy.StdCopy is the SDK's own demultiplexer for that framing, used here rather than hand-parsing the header format.
follow keeps the stream open and delivering new lines as the container produces them; without it, the stream ends once currently-buffered output is drained. since, if non-zero, asks Docker to only return lines at or after that time (a resume point for a caller that already ingested everything before it); the zero value returns everything Docker still has buffered.
The returned channels close together once the underlying log stream ends, for any reason: the container stopped, ctx was cancelled, or a read error occurred. A read error is reported on the error channel before both channels close; ctx cancellation and a clean stream end report no error, matching Events' own convention.
func (*Client) NetworkConnect ¶ added in v0.2.0
NetworkConnect implements Runtime. Inspects first (the same idempotency shape EnsureNetwork already uses) so a container already attached is a no-op, not an error from Docker's own "endpoint already exists" response. inspect.Containers is keyed by container ID regardless of what identifier the caller passed in (connectReferencedDatabases passes a name), so the membership check matches on both the map key and each entry's own Name field, confirmed live: a name-keyed-only check let a real "already exists" error through on the second call.
func (*Client) NetworkDisconnect ¶ added in v0.2.0
NetworkDisconnect implements Runtime. Not being attached is not an error, the same level-triggered reasoning RemoveNetwork's own doc comment gives. Confirmed live that the daemon's actual "not connected" response isn't classified as cerrdefs.IsNotFound (it comes back as a 403, the same status the "already exists" case on the connect side uses), so this also falls back to a substring match on its exact wording, the same defensive shape NetworkConnect's own fallback uses.
func (*Client) NetworkPoolUsage ¶
NetworkPoolUsage implements NetworkAdmin.
func (*Client) NodePlatform ¶
func (c *Client) NodePlatform(ctx context.Context) (NodePlatform, error)
NodePlatform implements ImageDetailInspector.
func (*Client) PauseContainer ¶ added in v0.2.0
PauseContainer freezes every process in a container (cgroup freezer) so its files stop changing without a restart.
func (*Client) Ping ¶
Ping is a thin liveness check against the Docker daemon, wrapping the underlying SDK client's own Ping. Deliberately a method on the concrete *Client, not a new Runtime method: Runtime has 6+ fake implementations across internal/reconcile's controllers and internal/agent's test files, plus the real client here and the agent-side remote transport, so adding a method there would ripple into all of them for the sake of one read-only health signal that GET /api/v1/system/status needs. internal/api.DockerPinger is the narrow interface that actually consumes this, satisfied structurally by *Client alone.
func (*Client) Prune ¶
func (c *Client) Prune(ctx context.Context, keep []string) PruneResult
Prune runs every cleanup stage this package supports, in order: stopped containers not in keep, dangling images, anonymous unused volumes, then unused build cache. Each stage is independent; a failure in one does not stop the others, and every stage's own failure (if any) is collected into Errors rather than aborting the call, so a caller always gets back whatever the other stages did manage to clean up.
keep is every container name the caller currently considers desired (every application replica's target container name plus every database's container name); see PruneContainers' own doc comment for exactly why this cannot be computed inside this package.
func (*Client) PruneAnonymousVolumes ¶
func (c *Client) PruneAnonymousVolumes(ctx context.Context) (PruneVolumesResult, error)
PruneAnonymousVolumes removes only anonymous volumes (Docker's own randomly-generated 64-character hex name, assigned when a container mounts a volume without one, e.g. a Dockerfile's own VOLUME instruction with no matching app.yaml volume declaration) that are not currently attached to any container, running or stopped.
This project's own EnsureVolume always creates a caller-named volume; internal/reconcile/database's dataVolumeName ("db-<name>-data") is the only volume name this project's reconciler itself ever produces. So restricting to the anonymous-name pattern is not merely mirroring the Docker CLI's own conservative default (`docker volume prune` without --all only removes anonymous volumes as of Engine API >= 1.42): it is a second, independent, daemon-version-proof guarantee that this method can never remove a database's data volume, because no name this project creates can ever match the anonymous pattern. Named volumes, even ones that look unused right now, are always left alone; a named volume can look briefly unattached mid-replace during a database engine version bump (internal/reconcile/database.Controller.replaceContainer: stop, remove, then create, strictly sequential) without actually being abandoned.
"Not currently attached to any container" is computed from a fresh ContainerList across every container, running or stopped, rather than trusting the Engine API's own dangling-volume filter, whose exact semantics (does it count a stopped container's mounts as still-attached?) are not something this method's safety should depend on being right.
Deliberately not scoped by this Client's own instance label (WithInstanceLabel), unlike PruneContainers: an anonymous volume is, by definition, Docker-generated and never passes through EnsureVolume, so it can never carry that label regardless of which instance's container triggered its creation. A label filter here would just make this method a permanent no-op. The "not attached to any container, any instance's or not" check above is what already makes this safe on a Docker daemon shared by more than one control-plane instance: mountedVolumeNames considers every container on the daemon, not just this instance's own.
func (*Client) PruneBuildCache ¶
func (c *Client) PruneBuildCache(ctx context.Context) (PruneBuildCacheResult, error)
PruneBuildCache removes unused BuildKit cache records from the daemon's embedded BuildKit instance, the same one internal/build's own Client drives (internal/build/client.go's own doc comment: BuildKit runs inside dockerd, reachable only via this same *dockerclient.Client's own /grpc hijack, so this daemon's build-cache accounting is this project's real build cache, not an unrelated one). All: false, matching `docker builder prune`'s own default, removes only cache not currently InUse: a cache record still backing an in-flight build is left alone rather than this call racing a build that happens to be running at the same moment an operator clicks "clean up now."
Deliberately not scoped by this Client's own instance label: BuildKit cache records carry no container/image identity at all to key an instance label off of, this project or any other's. The InUse check above is what already makes this safe to share a daemon: a build any control-plane instance has in flight keeps its cache records InUse regardless of which instance started it.
func (*Client) PruneContainers ¶
PruneContainers stops and removes every non-running container on this daemon whose name is not in keep, using a direct list-then-remove sequence (each already-stopped container removed individually) rather than the Docker Engine API's own bulk POST /containers/prune (ContainersPrune in the SDK). See this file's own package doc comment for why: that bulk endpoint cannot exclude specific containers by name, and this project's reconciler can consider a stopped container still desired (about to be restarted in place). keep must include every container name the caller currently considers live, e.g. every application replica's target container name plus every database's container name, or this can race a reconcile pass that's about to restart one of them.
Only containers in Docker's "exited" or "created" state are candidates, matching the scope Docker's own bulk container prune targets. "paused" and "restarting" are transient, mid-lifecycle states, never a stable "this is garbage" signal, so they're left alone entirely; "dead", a rare failure state of Docker's own container teardown, is left for an operator to investigate rather than silently swept up.
Since every candidate is already confirmed non-running by the status filter, removal never needs Force: true (that flag exists to stop a running container before removing it, not relevant here) and never sets RemoveVolumes: cascading volume deletion through container removal would bypass PruneAnonymousVolumes' own, more careful safety checks; volume cleanup is that method's job alone.
One narrow, self-healing race remains, worth naming rather than treating as impossible: internal/reconcile/application's createAndStart calls Create then Start as two separate steps, so a container briefly exists in Docker's own "created" (not yet running) state between them. The candidate filter above (status: exited or created) matches that window on purpose, since a container that never got past Create after a genuine failure is real cruft this should still remove. But if the caller's own keep set was computed before that exact container's name existed (a deploy landing in the same instant a manual prune call is already mid-flight), this could remove it. Level-triggered reconcile recovers on its own next pass (one wasted create, a logged error, no data loss), the same tolerance this codebase already extends to other narrow eventual-consistency windows (see internal/docker/client_live_test.go's waitForStopped), so this is accepted rather than solved with, e.g., a lock this package has no good way to hold across a caller-supplied keep computation anyway. Scoped to this Client's own instance label (WithInstanceLabel), when configured: without it, a stopped container from a different control- plane instance sharing this same Docker daemon would look identical to genuine cruft (this method has no name/brand filter of its own to begin with, only a status filter), and "not in my own keep list" would wrongly include literally every container the caller doesn't already know about, this instance's or not. See internal/spec.InstanceLabelKey's own doc comment.
func (*Client) PruneDanglingImages ¶
func (c *Client) PruneDanglingImages(ctx context.Context) (PruneImagesResult, error)
PruneDanglingImages removes only dangling images: untagged and not referenced by any container, Docker's own conservative default for `docker image prune` (no -a flag). The "dangling" filter is set explicitly here rather than relying on ImagesPrune's behavior with no filter at all, so this scope is guaranteed regardless of daemon version.
This deliberately never touches a tagged image, even one currently unreferenced by any container: this project's own rollback design keeps the previous N images pinned by tag so garbage collection cannot orphan a rollback target, and a tagged image sitting unused right now is exactly what that design depends on staying in place. The more aggressive "everything unused, tagged or not" cleanup `docker image prune -a` offers has no equivalent here, on purpose.
Deliberately not scoped by this Client's own instance label: a dangling image, by Docker's own definition, has zero tags left, so an instance label on it (even if one were set at build time) would say nothing about who still needs it. What actually makes this safe on a shared daemon is the Engine API's own ImagesPrune, which never removes an image still referenced by any container regardless of which control-plane instance owns that container.
func (*Client) RemoveImageByID ¶ added in v0.2.0
RemoveImageByID removes an image without force, so Docker itself refuses while anything still uses it. A missing image is not an error.
func (*Client) RemoveNetwork ¶
RemoveNetwork implements Runtime. Force-disconnects every still-attached container first: the Engine API refuses to remove a network with live endpoints, and a database container this codebase itself connected (internal/reconcile/application's connectReferencedDatabases) has no other path to get detached once the app that referenced it is gone.
func (*Client) RemoveNetworkIfEmpty ¶
RemoveNetworkIfEmpty implements NetworkAdmin: it never disconnects.
func (*Client) RemoveVolume ¶ added in v0.2.0
RemoveVolume deletes a single named Docker volume by name. Unlike PruneAnonymousVolumes, this takes an exact, caller-supplied name rather than discovering candidates itself: the caller (internal/api's handleCleanupOrphanedVolumes) has already done its own desired-state cross-reference and is asking for one specific, operator-confirmed volume, not "whatever currently looks unused."
func (*Client) ResolveImage ¶ added in v0.2.0
func (c *Client) ResolveImage(ctx context.Context, ref string, auth *RegistryAuth, requireFresh bool) (ResolvedImage, error)
ResolveImage implements ImageResolver.
func (*Client) RunOneShot ¶ added in v0.2.0
func (c *Client) RunOneShot(ctx context.Context, spec OneShotSpec) (OneShotResult, error)
RunOneShot runs spec to completion with default networking (scanners need egress to fetch their database) and every capability dropped. The container is force-removed on every path; the labels let an orphan sweep find leftovers of a control plane that died mid-run.
func (*Client) RunToCompletion ¶ added in v0.2.0
func (c *Client) RunToCompletion(ctx context.Context, spec ContainerSpec, maxOutput int) (RunOutcome, error)
RunToCompletion implements RunToCompletionRuntime through Create, so the job container gets the same hardening, image pull and network handling a service container does.
func (*Client) Runtime ¶ added in v0.2.0
func (c *Client) Runtime() RuntimeInfo
Runtime reports which container engine and privilege mode this Client is likely talking to. Consumed by GET /api/v1/system/doctor's container_runtime check through a narrow, consumer-defined interface, the same pattern ServerVersion's engineVersioner already establishes in internal/api.
func (*Client) RuntimeNames ¶ added in v0.2.0
RuntimeNames returns the OCI runtimes the Docker daemon has registered, used to detect whether the nvidia container runtime is installed.
func (*Client) ServerVersion ¶ added in v0.2.0
ServerVersion returns the Docker Engine version string the daemon reports.
func (*Client) StartBrowser ¶ added in v0.2.0
StartBrowser creates and starts spec's container and reports where its debug port is published. The image must already exist locally. The caller must Close the Browser; the container also removes itself once stopped.
func (*Client) Stats ¶
Stats fetches one resource-usage snapshot for the container with this ID, via Docker's one-shot stats endpoint (a single accurate sample, not a subscription): correct for a periodic 15s-resolution collector, unlike the streaming variant which is built for a live-updating `docker stats`-style display instead.
func (*Client) TagImage ¶ added in v0.2.0
TagImage points target at the image source names (a tag or image ID).
func (*Client) TestRegistryAuth ¶
TestRegistryAuth asks the daemon to authenticate against host with username/password, the daemon's own `docker login`-equivalent check, without pulling anything. Same narrow-interface-on-the-concrete-type reasoning as Ping above: internal/api.RegistryAuthTester is the consumer boundary, satisfied structurally by *Client alone.
func (*Client) UnpauseContainer ¶ added in v0.2.0
UnpauseContainer resumes a paused container.
func (*Client) UntagImage ¶ added in v0.2.0
UntagImage removes ref without force. Docker deletes the image only when ref was its last reference and no container uses it. A missing ref is not an error.
func (*Client) UpdateResources ¶
UpdateResources implements Runtime, via the Engine API's own ContainerUpdate call. This adjusts the container's cgroup limits in place; it never touches image, env, ports, mounts, or any other part of the container's configuration.
func (*Client) WatchContainer ¶
func (c *Client) WatchContainer(ctx context.Context, name string) (<-chan LifecycleEvent, <-chan error)
WatchContainer implements LifecycleWatcher.
type ClientOption ¶ added in v0.2.0
type ClientOption func(*Client)
ClientOption configures optional Client behavior at construction time.
func WithCreateDeclarer ¶ added in v0.2.0
func WithCreateDeclarer(d CreateDeclarer) ClientOption
WithCreateDeclarer reports every Create to d before it reaches Docker.
func WithHardening ¶ added in v0.2.0
func WithHardening(cfg HardeningConfig) ClientOption
WithHardening overrides the policy NewClient reads from the environment.
func WithHost ¶ added in v0.2.0
func WithHost(host string) ClientOption
WithHost makes the Client dial host (unix:///path) instead of the detected socket, keeping Runtime() describing the real daemon.
func WithInstanceLabel ¶ added in v0.2.0
func WithInstanceLabel(key, value string) ClientOption
WithInstanceLabel makes every resource this Client creates carry the Docker label key=value, and scopes this Client's own by-name lookups to resources carrying it. Callers pass internal/spec.InstanceLabelKey as key and store.GetOrCreateInstanceID's result as value, following the same "pass the resolved value in, don't import that package here" convention internal/reconcile/application.WithNetworkPrefix already establishes for brand.ShortName: this package stays spec- and store- agnostic. See InstanceLabelKey's own doc comment for why this exists: two control-plane instances can share one Docker daemon, and without this, one's cleanup pass can't tell its own resources from the other's.
func WithNetworkPool ¶
func WithNetworkPool(p NetworkPool) ClientOption
WithNetworkPool sets the pool explicitly (tests, embedders); NewClient otherwise reads APP_NETWORK_POOL.
type ContainerMount ¶ added in v0.2.0
ContainerMount is one mount: host Source at container Destination.
type ContainerNetwork ¶ added in v0.2.0
ContainerNetwork is one network attachment of a container.
type ContainerSpec ¶
type ContainerSpec struct {
Name string
Image string
Ports []PortBinding
Env map[string]string
Resources *Resources
// GPU, when non-nil, attaches NVIDIA GPUs through the nvidia
// container runtime. nil means no GPU device request.
GPU *GPURequest
// ShmSizeBytes sizes /dev/shm. 0 keeps Docker's default.
ShmSizeBytes int64
// Volumes are named Docker volumes to mount at create time. A
// database controller is the first caller; ordinary
// application containers leave this nil.
Volumes []VolumeMount
// BindMounts are real host directories to mount at create time
// (BindMount's own doc comment). Nil for every service before this
// field existed and for the ordinary stateless service today.
BindMounts []BindMount
// DNS lists nameserver IPs Docker writes into the container's
// /etc/resolv.conf, ahead of whatever the daemon would otherwise
// configure. Empty/nil is byte-identical to today: only a caller
// that resolved a real, container-reachable mesh DNS address
// (cmd/levelrail/mesh.go's containerDNSAddr) ever sets this, and
// only when the mesh is actually enabled and running.
DNS []string
// ExtraHosts are "name:ip" entries Docker writes into /etc/hosts, used
// to resolve a cross-node database's mesh name without a DNS server.
ExtraHosts []string
// Labels are applied to the container verbatim via the Engine API's
// own label mechanism (container.Config.Labels), operator-supplied
// custom Docker labels (internal/spec.Service.Labels, an escape
// hatch for external tooling that keys off container labels). This
// package stays spec-agnostic (same reasoning as Image being a
// plain string, not something spec-aware, this struct's own doc
// comment above): it trusts Labels arrived here already validated,
// the same way it already trusts Env and every other field. See
// internal/spec.ValidateLabels for what's rejected before a caller
// ever builds a ContainerSpec with this set. Client.Create merges in
// its own configured instance label (WithInstanceLabel) on top of
// whatever's here; that merge never mutates this map.
Labels map[string]string
// Network attaches the container to a non-default Docker network at
// create time, with Alias as the name sibling containers on that
// network can reach it by (Docker's embedded per-network DNS
// resolves it). Nil means Docker's default "bridge" network,
// unchanged from every container this codebase created before this
// field existed: that network has no embedded DNS, so this is what
// makes multi-service apps able to reach each other by name at all.
Network *NetworkAttachment
// RegistryAuth authenticates the image pull (Create's own ensureImage
// step) against a private registry. Nil means an unauthenticated
// pull, unchanged from every container this codebase created before
// this field existed.
RegistryAuth *RegistryAuth
// OnPull, when set, receives throttled image pull progress from Create.
// Not carried over the agent transport: a remote node pulls silently.
OnPull func(PullEvent)
// Command overrides the image's own default CMD. Nil means the
// image's own default, unchanged from every container this codebase
// created before this field existed. First caller:
// internal/reconcile/cloudflaretunnel, whose cloudflared image needs
// an explicit "tunnel run" argument rather than relying on the
// image's bare entrypoint.
Command []string
// Entrypoint overrides the image's own default ENTRYPOINT. Nil means
// the image's own default, unchanged from every container this
// codebase created before this field existed. First caller:
// internal/reconcile/registry, which needs to write an htpasswd file
// from injected env before handing off to the registry image's real
// entrypoint.
Entrypoint []string
// ForcePull skips ensureImage's local-presence check and always pulls
// Image fresh, even when a tag by that name already exists locally:
// the only way a caller can be sure a mutable tag (":latest" and
// similar) actually reflects a real upstream update rather than
// whatever Docker's own tag-based caching happened to keep around.
// False is unchanged from every container this codebase created
// before this field existed. Populated from
// store.DesiredService.PullPolicy == store.PullPolicyAlways.
ForcePull bool
// CapAdd lists Linux capabilities added beyond Docker's own default
// set (container.HostConfig.CapAdd), nil meaning none, unchanged from
// every container this codebase created before this field existed.
// First caller: internal/reconcile/application's egress sidecar,
// which needs NET_ADMIN to install iptables/ipset rules inside the
// network namespace it shares with the app container.
CapAdd []string
// NetworkMode sets container.HostConfig.NetworkMode directly, the
// Engine API's own string format (e.g. "container:<id>" to join
// another container's network namespace instead of getting one of
// its own). Empty leaves HostConfig.NetworkMode unset, Docker's
// default ("default", meaning the Network field above or the default
// bridge network), unchanged from every container this codebase
// created before this field existed. Mutually meaningful alongside
// Network only in the sense that Docker itself rejects combining a
// non-default NetworkMode with an EndpointsConfig attachment; callers
// that set NetworkMode leave Network nil.
NetworkMode string
// default is 1024). 0 keeps the default. Local nodes only; not carried
// over the agent transport.
CPUShares int64
// OomScoreAdj biases the kernel OOM killer toward this container
// (higher dies first). 0 keeps the default. Local nodes only.
OomScoreAdj int
// PidsLimit overrides the hardening pids limit when positive.
PidsLimit int64
// Hardened forces the strictest container profile whatever the global
// hardening mode: read-only root filesystem, every capability dropped
// with none added back, no-new-privileges, a PID limit and a small
// noexec /tmp. Used for untrusted workloads such as plugins.
Hardened bool
// User runs the container process as this uid[:gid]. Empty keeps the image's user.
User string
}
ContainerSpec is desired state for a container a controller wants to exist.
No restart policy field, deliberately: the reconciler, not Docker, is meant to be the sole authority on "should this container be running." A Docker-native restart policy running alongside a reconciler that also restarts dead containers is two independent systems racing to make the same decision, exactly the kind of drift the reconciler's level-triggered design exists to avoid. Every container Levelrail creates gets Docker's "no" restart policy; staying running is the reconciler's job, proven live in nginxdemo (Phase 0).
No health check field either: the app spec's readiness/liveness probes are modeled on Kubernetes' prober pattern (the controller calls out to the container, not the container reporting its own health via Docker's HEALTHCHECK state machine), the same design choice ADR 002's Consequences section already commits to over Coolify's confirmed weaker alternative (health check disabled by default, gated entirely on Docker's own HEALTHCHECK). The prober itself belongs in the application controller (Phase 1), not here; this package only needs to make a container reachable, via Ports above.
type ContainerState ¶
type ContainerState struct {
ID string
Name string
Image string
// ImageID is the local image ID the container actually runs, empty
// when the transport does not report it.
ImageID string
// Created is when the container was created, zero when unknown.
Created time.Time
Running bool
// Ports reflects live port bindings. Docker only actually binds a
// container's published ports once it's running, so this is empty
// for a created-but-not-yet-started container, not an error.
Ports []PortBinding
// ConfiguredPorts are the bindings the container was created with,
// read only for a stopped container (whose Ports is always empty) so a
// caller can still tell a stale port from a current one.
ConfiguredPorts []PortBinding
// Labels are the container's own Docker labels, verbatim, including
// internal/spec.InstanceLabelKey when the creating control-plane
// instance set one. Callers doing cleanup/stale-detection across a
// name-prefix match (internal/reconcile/application's
// staleContainers) use this to additionally confirm a matching-name
// container was actually created by this same instance before
// treating it as this service's own leftover; see
// InstanceLabelKey's own doc comment for why a name/brand match
// alone isn't enough on a Docker daemon shared by more than one
// control-plane instance.
Labels map[string]string
// Networks lists the Docker networks the container is attached to with
// its address on each. Only the local Docker client reports it.
Networks []ContainerNetwork
}
ContainerState is the observed state of a single container, trimmed to the fields a controller actually needs to decide what to do next.
type ContainerStats ¶
type ContainerStats struct {
// CPUPercent is 0-100 per core, computed from this one-shot
// response's own PreCPUStats, which some Docker installs (Docker
// Desktop's VM-backed daemon) leave empty. A periodic poller should
// use CPURaw plus its own previous sample instead (see CPUPercent
// the function below).
CPUPercent float64
// CPURaw is this sample's raw counters, for a caller that polls on
// an interval and keeps its own previous sample.
CPURaw CPUStatsRaw
// MemoryUsageBytes excludes page cache when the cgroup reports it
// separately (Stats above), matching `docker stats`' "used" figure
// rather than the raw cgroup usage counter, which double-counts
// reclaimable cache as "used."
MemoryUsageBytes uint64
MemoryLimitBytes uint64
// NetworkRxBytes/TxBytes are summed across every interface Docker
// reports for this container; a single container's total network
// usage is what the per-app metrics requirement asks for, not a
// per-interface breakdown.
NetworkRxBytes uint64
NetworkTxBytes uint64
// DiskReadBytes/WriteBytes are summed across every block device
// Docker's blkio accounting reports for this container's cgroup.
DiskReadBytes uint64
DiskWriteBytes uint64
}
ContainerStats is one point-in-time resource usage snapshot, trimmed and computed down to what the metrics store actually needs (CPU/memory/disk IO/network IO), same shape of simplification ContainerState already applies to Docker's raw container summary.
type CreateDeclaration ¶ added in v0.2.0
type CreateDeclaration struct {
Name string
HostNetwork bool
GPU bool
BindPaths []string
CapAdd []string
}
CreateDeclaration is what Create says a container needs beyond the hardened default, so a Docker API guard can tell a declared bind mount or GPU request from an injected one.
type CreateDeclarer ¶ added in v0.2.0
type CreateDeclarer interface {
DeclareCreate(CreateDeclaration) func()
}
CreateDeclarer receives a declaration before each container create; the returned func withdraws it once the create call returns.
type DaemonSecurity ¶ added in v0.2.0
type DaemonSecurity struct {
Rootless bool
// UsernsRemap means container root maps to an unprivileged host user.
UsernsRemap bool
Options []string
}
DaemonSecurity is what the daemon itself says about its isolation.
func ParseSecurityOptions ¶ added in v0.2.0
func ParseSecurityOptions(opts []string) DaemonSecurity
ParseSecurityOptions reads entries such as "name=rootless" and "name=userns" from the daemon's SecurityOptions.
type DiskUsage ¶
type DiskUsage struct {
ImagesTotalBytes int64
// ImagesReclaimableBytes follows Docker's own `docker system df`
// methodology (unused = zero containers reference the image,
// tagged or not), so an operator sees the same number the docker
// CLI would show them directly on this host. It is deliberately a
// bigger number than what PruneDanglingImages actually frees: that
// call only ever removes the dangling (untagged) subset, on
// purpose, see PruneDanglingImages' own doc comment for why a
// tagged-but-currently-unused image is a real rollback target this
// project has an explicit design rule to protect.
ImagesReclaimableBytes int64
ContainersTotalBytes int64
// ContainersReclaimableBytes sums the writable-layer size (SizeRw,
// not SizeRootFs, which would double-count shared image layers) of
// every non-running container, matching what PruneContainers is
// eligible to remove before its own additional "currently desired"
// exclusion is applied.
ContainersReclaimableBytes int64
VolumesTotalBytes int64
// VolumesReclaimableBytes sums every volume Docker itself reports
// as unreferenced by any container (RefCount == 0), a bigger set
// than PruneAnonymousVolumes actually touches: that call only
// removes the anonymous subset, never a named volume regardless of
// use, because a volume that looks unused can be one mid-recreate
// during a database engine version bump, see
// PruneAnonymousVolumes' own doc comment.
VolumesReclaimableBytes int64
BuildCacheTotalBytes int64
// BuildCacheReclaimableBytes sums every build cache record not
// currently InUse, matching what PruneBuildCache removes.
BuildCacheReclaimableBytes int64
}
DiskUsage is Docker's own storage accounting: space claimed by images, containers, volumes, and BuildKit's build cache (BuildKit runs embedded inside this daemon, see internal/build/client.go's own doc comment, so this daemon's build-cache accounting is the real number for this project's own builds too, not an unrelated cache).
This is a materially different number from GET /api/v1/system/status's existing DataDirTotalBytes/DataDirFreeBytes fields: those measure the control plane's own APP_DATA_DIR (app source checkouts, telemetry storage, the SQLite file), a plain host filesystem path with no relationship to where Docker itself stores image layers, container writable layers, or named volumes (typically under Docker's own data-root, e.g. /var/lib/docker). An operator whose data dir looks healthy can still be moments from a full disk because of accumulated Docker state, which is exactly the gap this reports.
type Event ¶
type Event struct {
Action EventAction
ContainerName string
Time time.Time
}
Event is a trimmed container lifecycle event from the Docker event stream. Reconcilers key off ContainerName, not ID, since a container's ID changes across recreates but the name a controller manages does not.
type EventAction ¶
type EventAction string
EventAction mirrors the subset of Docker container lifecycle events a reconciler cares about.
const ( EventStart EventAction = "start" EventDie EventAction = "die" EventStop EventAction = "stop" )
The lifecycle actions a reconciler can receive from Events.
type ExecExitError ¶
ExecExitError is the trailing error Exec's and ExecWithInput's returned io.ReadCloser produce, once their stream ends, when cmd exited non-zero inside the container: see Runtime.Exec's own doc comment for why an exit code is folded into the error a later Read returns rather than carried as a second out-of-band field. Every caller before this type existed (internal/backup's Dumper and Restorer) only ever needed "err != nil means the dump/restore failed," so ExitCode and Stderr lived only inside the error's formatted message, unrecoverable except by parsing text. This type keeps that exact message (Error() reproduces streamExecOutput's original format string byte for byte, so nothing that already checks err != nil or matches on message substrings, e.g. client_live_test.go's "exited 7" check, changes behavior) while making ExitCode and Stderr recoverable via errors.As, for a caller like internal/api's exec endpoint that has to report a real exit code and stderr back to an HTTP client instead of only "the command failed."
func (*ExecExitError) Error ¶
func (e *ExecExitError) Error() string
type ExecSession ¶ added in v0.2.0
ExecSession is one PTY-backed exec running inside a container: Read yields the terminal's output (stdout and stderr merged, as a real terminal merges them), Write feeds its input, Resize tells the process its window changed, and Close ends the session.
Read's trailing error carries the exec's own exit status the same way Runtime.Exec's does: io.EOF for a clean exit, *ExecExitError for a non-zero one. Close is best effort at stopping the remote process: the Engine API has no "kill this exec," so closing the PTY is the only lever, which ends a shell but cannot end a process that ignores its terminal hanging up.
type ExecTTYOptions ¶ added in v0.2.0
ExecTTYOptions configures one interactive exec. Cmd is required; Env carries the terminal's own environment (TERM above all, without which most full-screen programs refuse to draw).
type ExitState ¶ added in v0.2.0
type ExitState struct {
Running bool
OOMKilled bool
ExitCode int
// RestartCount and StartedAt are zero when the transport cannot report
// them (the remote agent protocol does not carry them yet).
RestartCount int
StartedAt time.Time
// FinishedAt is when the container last stopped; zero while running or
// when the transport cannot report it.
FinishedAt time.Time
}
ExitState is a container's exit-relevant state: whether it's still running, and if not, why. Deliberately its own type rather than more fields on ContainerState: getting OOMKilled/ExitCode requires a real Docker inspect call, not the ContainerList-based summary InspectByName and ListByPrefix already use (container.Summary carries no such fields), and every Runtime consumer that only ever checked ContainerState.Running has no reason to pay for that extra call.
type ExitStateInspector ¶ added in v0.2.0
type ExitStateInspector interface {
// InspectExitState returns the container's exit state, or (nil, nil)
// if no such container exists, the same "not found is a valid
// observed state" contract InspectByName documents below.
InspectExitState(ctx context.Context, name string) (*ExitState, error)
}
ExitStateInspector is an optional Runtime capability, checked via a type assertion rather than added to the Runtime interface itself: doing the latter would force every hand-written test fake across the reconcile packages to grow a method most of them have no use for. Both real transports implement it (*Client locally, internal/agent's GRPCTransport over the wire); a runtime lacking it just skips the fail-fast-on-crash check and falls back to waiting out the full readiness budget.
type GPURequest ¶ added in v0.2.0
GPURequest asks Docker for NVIDIA GPUs via a DeviceRequest. Count -1 means every GPU; DeviceIDs (indexes or UUIDs) and Count are mutually exclusive, DeviceIDs wins when both are set.
type HardeningConfig ¶ added in v0.2.0
type HardeningConfig struct {
Mode HardeningMode
// PidsLimit is the per-container process cap; 0 or negative disables it.
PidsLimit int64
// ExtraCaps are operator-granted capabilities added on top of
// MinimalCapabilities for every container.
ExtraCaps []string
}
HardeningConfig is the effective container hardening policy.
func HardeningFromEnv ¶ added in v0.2.0
func HardeningFromEnv(runtime RuntimeInfo) (HardeningConfig, error)
HardeningFromEnv reads the hardening policy from the environment. On an invalid value it returns the safe default (enforce) together with the error. runtime.Rootless auto-disables PidsLimit unless APP_CONTAINER_PIDS_LIMIT is set explicitly: rootless Docker without a delegated cgroup v2 controller rejects a pids-limit HostConfig at container-create time, so imposing it unconditionally would break every deploy on such a host. CapDrop, CapAdd and no-new-privileges are unaffected: those operate inside the container's own user namespace regardless of rootless mode, so nothing needs to come off the minimal capability list for Podman or rootless Docker.
func (HardeningConfig) Report ¶ added in v0.2.0
func (h HardeningConfig) Report() HardeningReport
Report returns what enforce sets, and whether it is actually applied.
type HardeningMode ¶ added in v0.2.0
type HardeningMode string
HardeningMode selects whether secure container defaults are applied.
const ( // HardeningOff applies nothing and reports nothing. HardeningOff HardeningMode = "off" // HardeningWarn applies nothing to containers; diagnostics report what // enforce would set. Explicit opt-out of the enforce default. HardeningWarn HardeningMode = "warn" // HardeningEnforce (the default) drops all capabilities except the minimal set, sets // no-new-privileges and a PID limit on every container Create makes. HardeningEnforce HardeningMode = "enforce" )
func ParseHardeningMode ¶ added in v0.2.0
func ParseHardeningMode(s string) (HardeningMode, error)
ParseHardeningMode validates a mode string; empty means the default.
type HardeningReport ¶ added in v0.2.0
type HardeningReport struct {
Mode HardeningMode
Applied bool
CapDrop []string
CapAdd []string
NoNewPrivileges bool
PidsLimit int64
}
HardeningReport describes what enforce sets, for the doctor endpoint.
type ImageDetailInspector ¶
type ImageDetailInspector interface {
InspectImageDetails(ctx context.Context, ref string) (*ImageDetails, error)
NodePlatform(ctx context.Context) (NodePlatform, error)
}
ImageDetailInspector is an optional Runtime capability (type-asserted, like ExitStateInspector) used only to enrich a deploy trace.
type ImageDetails ¶
type ImageDetails struct {
ID string
Arch, OS string
SizeBytes int64
Entrypoint []string
Cmd []string
ExposedPorts []string
HasHealthcheck bool
Healthcheck []string
}
ImageDetails is what a deploy trace reports about a local image.
type ImageInfo ¶
ImageInfo is one tagged image available locally, used to discover rollback candidates, per the rule that the previous N images stay pinned so garbage collection cannot orphan a rollback target.
type ImageInspector ¶ added in v0.2.0
ImageInspector reports the local image ID a reference resolves to on the node, empty when the image is not present there.
type ImageLayerInspector ¶ added in v0.2.0
type ImageLayerInspector interface {
InspectImageLayers(ctx context.Context, ref string) ([]string, error)
}
ImageLayerInspector reports the rootfs layer diff IDs of a local image, which are identical across image stores for the same content (unlike the image ID). It returns nil when the image is not present.
type ImageLoader ¶ added in v0.2.0
ImageLoader loads a `docker save` tar stream into the node's image store.
type ImagePinner ¶ added in v0.2.0
type ImagePinner interface {
TagImage(ctx context.Context, source, target string) error
UntagImage(ctx context.Context, ref string) error
}
ImagePinner adds and removes extra tags on local images, so content a caller still needs never becomes dangling and eligible for pruning.
type ImageResolver ¶ added in v0.2.0
type ImageResolver interface {
ResolveImage(ctx context.Context, ref string, auth *RegistryAuth, requireFresh bool) (ResolvedImage, error)
}
ImageResolver resolves a tag to content at deploy time. requireFresh turns a registry failure into an error instead of a cache fallback.
type ImageSource ¶ added in v0.2.0
type ImageSource string
ImageSource says where ResolveImage got an image's content identity.
const ( // ImageSourcePinned means the reference already carried a digest. ImageSourcePinned ImageSource = "pinned" // ImageSourceRegistry means the registry answered for the tag. ImageSourceRegistry ImageSource = "registry" // ImageSourceCache means the registry failed and the local image was used. ImageSourceCache ImageSource = "cache" // ImageSourceNone means neither the registry nor the local cache knew the tag. ImageSourceNone ImageSource = "none" )
type LabeledContainer ¶ added in v0.2.0
LabeledContainer is a container found by label, in any state.
type LifecycleEvent ¶
type LifecycleEvent struct {
// Action is create, start, die, oom, kill, stop or health_status.
Action string
Health string
ExitCode int
HasExit bool
OOMKilled bool
Time time.Time
}
LifecycleEvent is one daemon event for a watched container.
type LifecycleWatcher ¶
type LifecycleWatcher interface {
WatchContainer(ctx context.Context, name string) (<-chan LifecycleEvent, <-chan error)
}
LifecycleWatcher is an optional Runtime capability: the daemon's own event stream for one container, so a trace shows what the daemon actually did.
type LocalContainer ¶ added in v0.2.0
type LocalContainer struct {
Name string
Image string
Running bool
Networks []LocalNetwork
// Ports are every port the image exposes, published or not.
Ports []int
// Published are the host ports this container publishes.
Published []int
Mappings []PortMapping
Mounts []ContainerMount
NetworkMode string
Args []string
ExtraHosts []string
}
LocalContainer is a container on this Docker daemon with its networks and ports, used to find a database to migrate from or a reverse proxy holding the host's web ports. Args and ExtraHosts are only filled by InspectLocalContainer.
type LocalNetwork ¶ added in v0.2.0
type LocalNetwork struct {
Name string
IP string
// Gateway is the host side of the network, reachable from the container.
Gateway string
}
LocalNetwork is one network a container is attached to and the address it has there.
type LogLine ¶
type LogLine struct {
// Stream is "stdout" or "stderr".
Stream string
// Timestamp is Docker's own recorded time for this line, parsed from
// the log stream (Logs always requests Timestamps: true). Zero if the
// line didn't parse as the expected "<timestamp> <message>" shape;
// callers should fall back to their own clock in that case rather
// than treating a parse failure as fatal.
Timestamp time.Time
Message string
}
LogLine is one demultiplexed, timestamped line of container log output.
type NamedVolume ¶ added in v0.2.0
type NamedVolume struct {
Name string
SizeBytes int64 // -1 if the volume driver doesn't report size
CreatedAt string // Docker's own volume creation timestamp, RFC3339
// Mounted reports whether this volume is currently attached to any
// container, running or stopped, the same mountedVolumeNames signal
// PruneAnonymousVolumes already relies on. A caller can use this as
// a second, independent guard on top of desired-state absence: this
// project's own containers are only ever created from desired state,
// so a volume neither in desired state nor mounted anywhere is
// orphaned by two independent signals, not one.
Mounted bool
}
NamedVolume is one of this project's own named (non-anonymous) Docker volumes, the candidate set GET /api/v1/system/volumes/orphaned filters down to "genuinely orphaned" by cross-referencing against current desired state. That cross-reference deliberately does not live here: see this file's package doc comment in prune.go for why keep-set/ desired-state computation belongs to internal/api, which has store access, not internal/docker.
type NetworkAdmin ¶
type NetworkAdmin interface {
DescribeNetworks(ctx context.Context, prefix string) ([]NetworkDetail, error)
RemoveNetworkIfEmpty(ctx context.Context, name string) (bool, error)
NetworkPoolUsage(ctx context.Context) (PoolUsage, error)
}
NetworkAdmin is the optional surface a local Docker client offers on top of Runtime; remote node transports do not implement it.
type NetworkAttachment ¶
NetworkAttachment is a container's non-default network attachment: which Docker network to join, and what DNS-resolvable alias sibling containers on that network can reach it by.
type NetworkDetail ¶
type NetworkDetail struct {
ID string
Name string
Labels map[string]string
Subnets []string
Created time.Time
Attached int
}
NetworkDetail is one product network with the facts GC and the diagnostics API need.
type NetworkInfo ¶
type NetworkInfo struct {
ID string
Name string
// Labels are the network's own Docker labels, verbatim. Same
// cross-instance-ownership purpose as ContainerState.Labels above:
// NetworkCleanupController checks internal/spec.InstanceLabelKey
// here before treating a matching-name network as an orphan of this
// instance's own apps.
Labels map[string]string
}
NetworkInfo is one Docker network, trimmed to what ListNetworksByPrefix's caller needs to decide whether it's still wanted.
type NetworkPool ¶
NetworkPool is the address block explicit per-network subnets come from.
func ParseNetworkPool ¶
func ParseNetworkPool(s string) (pool NetworkPool, ok bool, err error)
ParseNetworkPool parses an APP_NETWORK_POOL value; empty means the default. "off" returns ok=false so the daemon's own default pools are used.
func (NetworkPool) Allocate ¶
Allocate returns the first /24 in the pool overlapping nothing in used.
func (NetworkPool) Capacity ¶
func (p NetworkPool) Capacity() int
Capacity is how many /24 networks fit in the pool.
type NetworkShareVolumeOpts ¶ added in v0.2.0
type NetworkShareVolumeOpts struct {
}
NetworkShareVolumeOpts is the resolved input NetworkShareDriverOpts needs: a CIFS share's Password comes from internal/secrets, already decrypted by the caller, and is never logged or stored by this package.
type NodePlatform ¶
type NodePlatform struct {
Arch, OS string
}
NodePlatform is the platform the daemon runs containers on.
type OneShotResult ¶ added in v0.2.0
OneShotResult is what a one-shot container produced.
type OneShotSpec ¶ added in v0.2.0
type OneShotSpec struct {
Name string
Image string
Command []string
Env map[string]string
Labels map[string]string
// Files are copied into the container before it starts, keyed by absolute path.
Files map[string][]byte
// Volumes maps a named volume to its mount path; each is created if missing.
Volumes map[string]string
MemoryBytes int64
NanoCPUs int64
PidsLimit int64
// MaxOutput caps captured stdout and stderr each; 0 means 32 MiB.
MaxOutput int64
}
OneShotSpec describes a run-to-completion container: it is created, started, waited on, read and force-removed by RunOneShot.
type PoolExhaustedError ¶
PoolExhaustedError is returned when no subnet could be allocated for a new network. Its message carries the numbers and the fix.
func (*PoolExhaustedError) Error ¶
func (e *PoolExhaustedError) Error() string
type PortBinding ¶
type PortBinding struct {
ContainerPort int
HostPort int
// HostIP is the interface a published port binds to. Empty is
// treated as 0.0.0.0 (every interface) by Create, matching Docker's
// own default: resolving the "private"/"public"/literal-IP shorthand
// this maps from (internal/bindaddr) into a concrete IP is the
// caller's job, the same "translation is the caller's job, keep this
// package spec-agnostic" reasoning Resources' own doc comment gives.
HostIP string
// Protocol is "tcp" or "udp". Empty is treated as "tcp" by Create.
Protocol string
}
PortBinding maps one container port to a host port. In a ContainerSpec, HostPort of 0 means "let Docker assign one"; in an observed ContainerState it's always the concrete port actually bound, never 0.
Host ports, not direct container-IP routing, on purpose: this process runs as a plain host process today (the agent's single-node mode, where control plane and agent share a process), and container IPs on Docker's bridge network are only reachable from the host on Linux, not from Docker Desktop's macOS VM boundary. Every target this matters for (a Linux managed node, and this Mac during development) can always reach localhost:hostPort, so that's the one mechanism used everywhere rather than branching on platform.
type PortMapping ¶ added in v0.2.0
PortMapping is one published port: container Private on host Public.
type PruneBuildCacheResult ¶
PruneBuildCacheResult is one PruneBuildCache call's outcome.
type PruneContainersResult ¶
PruneContainersResult is one PruneContainers call's outcome.
type PruneImagesResult ¶
PruneImagesResult is one PruneDanglingImages call's outcome.
type PruneResult ¶
type PruneResult struct {
ContainersRemoved []string
ContainersReclaimedBytes uint64
ImagesRemoved []string
ImagesReclaimedBytes uint64
VolumesRemoved []string
VolumesReclaimedBytes uint64
BuildCacheRemoved []string
BuildCacheReclaimedBytes uint64
// Errors collects any stage's failure without aborting the rest:
// the same "one broken resource must not block others" principle
// internal/reconcile/application.Controller.removeContainers
// already applies to stale-container cleanup, applied here across
// prune stages instead of across containers.
Errors []string
}
PruneResult is what one Prune call actually did, returned as a single aggregate report so a caller (handleSystemPrune) can show an operator exactly what was removed, not just "done."
type PruneVolumesResult ¶
PruneVolumesResult is one PruneAnonymousVolumes call's outcome.
type PullConfig ¶
PullConfig throttles pull reporting so a many-layer image cannot flood a log.
func PullConfigFromEnv ¶
func PullConfigFromEnv(lookup func(string) (string, bool)) PullConfig
PullConfigFromEnv reads the throttle settings; bad values keep the default.
type PullEvent ¶
type PullEvent struct {
Phase PullPhase
Ref string
// Aggregate view, set on Progress and Done.
Layers, LayersDone, Extracting, Existing int
BytesCurrent, BytesTotal int64
Percent int
// Layer view, set on PullPhaseLayer.
LayerID, LayerState string
LayerCurrent int64
LayerTotal int64
// Final status line and digest, set on Done.
Status string
Digest string
}
PullEvent is one throttled report from an image pull.
type PullTracker ¶
type PullTracker struct {
// contains filtered or unexported fields
}
PullTracker folds the daemon's JSON pull stream into throttled events.
func NewPullTracker ¶
func NewPullTracker(ref string, cfg PullConfig, emit func(PullEvent)) *PullTracker
NewPullTracker builds a tracker that reports through emit.
func (*PullTracker) Consume ¶
func (p *PullTracker) Consume(r io.Reader) error
Consume reads the whole stream. A daemon-reported error (auth denied, a layer failing mid-download) is returned with the daemon's own text.
func (*PullTracker) Malformed ¶
func (p *PullTracker) Malformed() int
Malformed counts stream lines that were not valid JSON.
type RegistryAuth ¶
RegistryAuth is a plaintext username/password pair for pulling a private image, resolved by the caller (internal/reconcile/application) from internal/secrets immediately before Create; never persisted by this package.
type ResolvedImage ¶ added in v0.2.0
type ResolvedImage struct {
// Ref is what to create containers from: the input pinned to Digest
// when a registry digest is known, otherwise the input unchanged.
Ref string
// Digest is the registry digest (a manifest list digest for a
// multi-arch image), empty when only a local image ID is known.
Digest string
// LocalID is the local image ID when the image was found only locally.
LocalID string
Source ImageSource
// RegistryErr is why the registry lookup failed, for Cache and None.
RegistryErr error
}
ResolvedImage is a floating reference turned into content.
type Resources ¶
type Resources struct {
MemoryBytes int64
NanoCPUs int64
// SwapMemoryBytes is Docker's own MemorySwap: total memory plus swap
// combined, only meaningful alongside MemoryBytes.
SwapMemoryBytes int64
// CPUSetCPUs pins the container to specific host CPUs, Docker's own
// cpuset-cpus format (e.g. "0-3" or "0,2").
CPUSetCPUs string
}
Resources caps a container's memory and CPU, in the Engine API's own units directly (bytes, nano-CPUs) rather than an invented "cores as float64" indirection. Translating from app.yaml's human-friendly units (spec.Resources: "512Mi", 0.5 cores) into these is the caller's job, keeping this package spec-agnostic, same as ContainerSpec.Image being a plain string rather than something spec-aware.
type RunOutcome ¶ added in v0.2.0
type RunOutcome struct {
ContainerID string
ExitCode int
OOMKilled bool
// Output is the tail of the container's combined stdout and stderr.
// OutputCaptured is false when the node's transport cannot return logs.
Output string
OutputCaptured bool
}
RunOutcome is what a run-to-completion container produced.
func RunToCompletionVia ¶ added in v0.2.0
func RunToCompletionVia(ctx context.Context, rt Runtime, spec ContainerSpec, maxOutput int) (RunOutcome, error)
RunToCompletionVia runs spec on rt, using its native implementation when it has one and otherwise create, start and poll through the plain Runtime surface (exit code only, no output), which is all a remote agent exposes.
type RunToCompletionRuntime ¶ added in v0.2.0
type RunToCompletionRuntime interface {
RunToCompletion(ctx context.Context, spec ContainerSpec, maxOutput int) (RunOutcome, error)
}
RunToCompletionRuntime is an optional Runtime capability: create, start, wait for and remove a container built from a full ContainerSpec, returning its exit code and output. The error is context.Canceled or context.DeadlineExceeded (wrapped) when ctx ended the run; the container is killed and removed on every path.
type Runtime ¶
type Runtime interface {
// InspectByName returns the current state of the container with this
// name, or (nil, nil) if no such container exists. It never returns
// an error for "not found": that's a valid observed state, not a
// failure.
InspectByName(ctx context.Context, name string) (*ContainerState, error)
// Create makes a container from spec but does not start it. Returns
// the new container's ID.
Create(ctx context.Context, spec ContainerSpec) (id string, err error)
// Start starts an existing container by ID.
Start(ctx context.Context, id string) error
// Events streams container lifecycle events until ctx is cancelled.
// The error channel receives at most one error (a stream failure)
// and is then closed; the event channel is closed once the stream
// ends for any reason.
Events(ctx context.Context) (<-chan Event, <-chan error)
// ListImages returns every locally-present tag under repo (e.g.
// "levelrail/myapp"), newest first, for rollback candidate discovery.
// An empty result and a nil error both mean "no images found", not
// an error: a service that's never been built has no images yet.
ListImages(ctx context.Context, repo string) ([]ImageInfo, error)
// ListByPrefix returns every container (running or not) whose name
// starts with prefix, for a controller to find every container
// belonging to a service it manages, not just the one canonical name
// nginxdemo's simpler world got away with.
ListByPrefix(ctx context.Context, prefix string) ([]ContainerState, error)
// Stop stops a running container, sending Signal (empty means
// Docker's default, SIGTERM) and waiting up to timeout before
// forcing it. A negative timeout waits indefinitely.
Stop(ctx context.Context, id string, timeout time.Duration) error
// Remove deletes a container. force also removes a still-running
// one (stopping it first); without force, removing a running
// container is an error, matching Docker's own default.
Remove(ctx context.Context, id string, force bool) error
// UpdateResources applies new memory, CPU, swap, and cpuset limits to
// an already-running container in place, via the Engine API's own
// ContainerUpdate call. Unlike every other resource-affecting change
// a controller can want (image, env, ports, health check), this one
// has a real live-update path: Docker itself supports adjusting a
// running container's cgroup limits without stopping it, so a
// reconciler can converge a resource-limit-only diff without the
// blue-green cutover a full recreate would otherwise force.
UpdateResources(ctx context.Context, id string, resources Resources) error
// EnsureVolume creates a named Docker volume if it doesn't already
// exist. Idempotent: calling it for a volume that's already present
// is not an error, matching the Engine API's own VolumeCreate
// semantics (creating by an existing name returns that volume
// unchanged rather than failing).
EnsureVolume(ctx context.Context, name string) error
// EnsureNetwork creates a Docker bridge network named name if it
// doesn't already exist, returning its ID either way. Idempotent:
// inspect first, create only on a genuine miss, the same shape the
// real implementation's ensureImage already uses for images.
// Reconcile calls happen often (event-driven plus periodic resync),
// so this must be safe to call on every pass, not just the first.
EnsureNetwork(ctx context.Context, name string) (id string, err error)
// RemoveNetwork deletes a Docker network by name. Removing a network
// that doesn't exist is not an error: cleanup is level-triggered and
// may run against state that's already converged.
RemoveNetwork(ctx context.Context, name string) error
// ListNetworksByPrefix returns every Docker network whose name
// starts with prefix, for a reconciler to diff observed networks
// against current desired state (internal/reconcile/application's
// NetworkCleanupController).
ListNetworksByPrefix(ctx context.Context, prefix string) ([]NetworkInfo, error)
// NetworkConnect attaches containerID to network by name. Idempotent:
// already being attached is not an error, since a reconciler calls
// this on every pass, not just once.
NetworkConnect(ctx context.Context, network, containerID string) error
// NetworkDisconnect detaches containerID from network by name. Not
// being attached is not an error, for the same level-triggered
// reason RemoveNetwork's own doc comment gives. force matches the
// Engine API's own NetworkDisconnect force flag: detach even if the
// container believes it's still using that network.
NetworkDisconnect(ctx context.Context, network, containerID string, force bool) error
// Exec runs cmd inside the already-running container containerID and
// returns its stdout as a stream, using the Engine API's exec
// facility (ContainerExecCreate then ContainerExecAttach), the same
// two calls `docker exec` itself is built on. This is the one Runtime
// method that reaches into a process running inside a container
// rather than managing the container's own lifecycle; it exists for
// internal/backup's Dumper, which has no other way to run a
// database's own native dump tool (pg_dump, mysqldump, redis-cli)
// against a live container without shelling out, which is exactly
// what every other method on this interface already exists to avoid.
//
// The returned ReadCloser carries only cmd's stdout. Stderr is
// captured by the implementation and, if cmd exits non-zero,
// surfaced as the error a later Read returns once the stream ends:
// the same "an error can arrive with or after the final bytes"
// contract every io.Reader already promises, so a caller only ever
// has one place to check for failure instead of a second out-of-band
// exit-code field. Close must be called once the caller is done with
// the stream, success or failure alike, to release the underlying
// exec connection.
Exec(ctx context.Context, containerID string, cmd []string) (io.ReadCloser, error)
// ExecWithInput is Exec plus a stdin stream: it runs cmd inside
// containerID exactly as Exec does (same stdout/stderr handling, same
// exit-code-becomes-a-trailing-error contract), except cmd's stdin is
// wired to stdin instead of being left unattached. This exists for
// internal/backup's Restorer, which needs to pipe a downloaded dump
// into psql/mysql running inside a live container; Exec itself can't
// do this; ContainerExecCreate has to be told AttachStdin: true up
// front for the exec session to have a stdin pipe at all, and every
// other caller of Exec (ContainerDumper) never needs one, so this is a
// second method rather than a third parameter Exec's own existing
// call site would otherwise have to grow to accommodate.
//
// Implementations read stdin to completion (EOF) and then signal
// end-of-input to the exec'd process the way closing stdin on a real
// terminal would, before returning; a command like psql that reads
// until EOF and then exits depends on that signal to ever finish, not
// only on stdin's bytes arriving. Callers are not required to provide
// a stdin that reaches EOF on its own if the command they're running
// doesn't need one; passing an empty reader is equivalent to Exec's
// own "nothing attached to stdin" behavior for that command.
//
// The returned ReadCloser carries stdout, identically to Exec's; Close
// must be called once done, same contract.
ExecWithInput(ctx context.Context, containerID string, cmd []string, stdin io.Reader) (io.ReadCloser, error)
}
Runtime is the surface reconcile controllers are allowed to depend on. The real implementation (Client, in client.go) talks to the Docker Engine API. Tests use a hand-written fake: see nginxdemo's test file for the pattern every future controller's tests should follow.
type RuntimeInfo ¶ added in v0.2.0
type RuntimeInfo struct {
Kind RuntimeKind
Rootless bool
// Host is the docker host URL (unix://... or tcp://...) this was
// classified from.
Host string
// Source is the env var name that supplied Host, or "detected" for a
// well-known socket path found on disk, or "default" for the
// standard rootful fallback.
Source string
}
RuntimeInfo is DetectRuntimeSocket's result: which engine and privilege mode this control plane is likely talking to, and which setting decided the socket.
func DetectRuntimeSocket ¶ added in v0.2.0
func DetectRuntimeSocket(lookupEnv func(string) (string, bool)) RuntimeInfo
DetectRuntimeSocket resolves the Docker Engine API host NewClient would connect to, in the same precedence order NewClient itself applies: APP_CONTAINER_RUNTIME_SOCKET, then DOCKER_HOST, then a well-known rootless or Podman socket path, then the standard rootful default. It classifies engine (Docker vs Podman) and privilege mode (rootless vs rootful) from that path alone, a heuristic based on well-documented conventions (XDG_RUNTIME_DIR, /run/user/<uid>, Podman's socket name): it never dials the daemon, so a live Docker client's own ServerVersion is the only verified corroborating signal available.
type RuntimeKind ¶ added in v0.2.0
type RuntimeKind string
RuntimeKind is the container engine DetectRuntimeSocket believes it is talking to, decided from the socket path alone.
const ( RuntimeKindDocker RuntimeKind = "docker" RuntimeKindPodman RuntimeKind = "podman" RuntimeKindUnknown RuntimeKind = "unknown" )
RuntimeKind values DetectRuntimeSocket can return.
type ServiceUser ¶ added in v0.2.0
ServiceUser describes how this process can reach the daemon socket.
func CurrentServiceUser ¶ added in v0.2.0
func CurrentServiceUser() ServiceUser
CurrentServiceUser reports whether this process runs as root or holds the docker group, either of which makes the socket root equivalent.
type StatsInspector ¶ added in v0.2.0
type StatsInspector interface {
Stats(ctx context.Context, containerID string) (ContainerStats, error)
}
StatsInspector is an optional Runtime capability for reading one-shot container resource usage, checked via a type assertion rather than added to Runtime itself, the same reasoning ExitStateInspector's own doc comment gives (runtime.go): *Client satisfies it structurally already, and internal/agent's GRPCTransport implements it by dispatching to the remote node's own *Client over the wire.
type TTYRuntime ¶ added in v0.2.0
type TTYRuntime interface {
ExecTTY(ctx context.Context, containerID string, opts ExecTTYOptions) (ExecSession, error)
}
TTYRuntime is the interactive half of Runtime's exec surface, kept separate because Runtime is implemented by a dozen narrow test fakes that have no interactive exec to offer and no reason to grow a stub for one. Every real implementation (docker.Client, the agent's gRPC transport, and the in-process transport wrapping either) implements both, so a caller resolving a node's Runtime type-asserts to this and reports "not supported on this node" if it fails.
type TTYSize ¶ added in v0.2.0
TTYSize is a terminal's character grid. Zero in either field means "let the daemon pick," the same as leaving Docker's own ConsoleSize unset.
type VolumeMount ¶
type VolumeMount struct {
Name string
ContainerPath string
// ReadOnly mounts the volume read-only inside the container. False
// (read-write) for every caller before internal/backup's volume
// archiver started setting it explicitly true, so this is
// byte-identical to every mount created before this field existed.
ReadOnly bool
}
VolumeMount attaches one named Docker volume to a path inside a container, e.g. Postgres's /var/lib/postgresql/data or Redis's /data. Name is a Docker volume name, not a host path: bind mounts aren't exposed here, keeping this package's surface to what a single-node managed database actually needs today.
Source Files
¶
- archive.go
- browser.go
- client.go
- declare.go
- exec_tty.go
- gpu_attach.go
- hardening.go
- image_digest.go
- image_load.go
- image_pin.go
- local_containers.go
- logs.go
- netadmin.go
- netpool.go
- network_share.go
- oneshot.go
- pause.go
- privilege.go
- prune.go
- pull_progress.go
- rootless.go
- run_to_completion.go
- runtime.go
- stats.go
- trace_inspect.go
- volume_owner.go
- volumes_orphaned.go
- volumes_prefix.go