docker

package
v0.1.36 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 26 Imported by: 0

Documentation

Overview

Package docker wraps the Docker Engine SDK behind a small interface, per the architecture plan (Docker Engine via the official SDK behind an interface). The reconcile loop depends only on the Client interface here, so it can be unit tested with a fake, whilst the concrete engineClient talks to a real Docker daemon.

Every container the agent starts carries our label prefix (LabelPrefix) so we can enumerate exactly the containers we own and never touch anything else on the host. Every container is created with hard limits on every axis: memory, CPUs, PIDs, a read-only root filesystem, dropped capabilities, no-new-privileges, and a shared bridge with inter-container communication disabled so co-located Eggs cannot reach one another. Published ports bind to loopback by default, so an Egg is reachable only through the local ingress. There is deliberately no egress or bandwidth cap: egress is unmetered on every tier.

Index

Constants

View Source
const (
	// LabelManaged marks a container as owned by the Ruust agent.
	LabelManaged = LabelPrefix + ".managed"
	// LabelWorkloadID carries the WorkloadSpec.ID.
	LabelWorkloadID = LabelPrefix + ".workload"
	// LabelBlobID carries the internal Blob identifier (never shown to
	// customers).
	LabelBlobID = LabelPrefix + ".blobid"
	// LabelEggID carries the customer-facing Egg identifier.
	LabelEggID = LabelPrefix + ".eggid"
	// LabelVersion carries the desired-state version this container was created
	// for, so a changed spec forces a roll.
	LabelVersion = LabelPrefix + ".version"
	// LabelImageRef carries the image reference this container was created for.
	LabelImageRef = LabelPrefix + ".imageref"
	// LabelPort carries the port the app is expected to listen on, so the health
	// probe knows where to connect.
	LabelPort = LabelPrefix + ".port"
	// LabelHealthPath carries the HTTP path the health probe requests.
	LabelHealthPath = LabelPrefix + ".healthpath"
	// LabelHealthProbe carries how the container is health-checked: "http", "tcp" or
	// "running". Empty keeps the volume/port heuristic.
	LabelHealthProbe = LabelPrefix + ".healthprobe"
	// LabelReplica carries the 0-based replica index of this container within its
	// workload, so a single Egg can run several load-balanced replicas, each in its
	// own container with its own identity and (for web Eggs) its own host port.
	LabelReplica = LabelPrefix + ".replica"
)

Label keys the agent stamps onto every container it owns.

View Source
const EggBridgeName = "ruust-eggs0"

EggBridgeName is the fixed host bridge interface for EggNetwork. Pinning it (rather than letting Docker generate a br-<id> name) gives the host egress firewall a stable interface to match on, so it can block Egg traffic to the cloud metadata endpoint and the private network. See infra/provisioning/firewall/egg-egress.sh.

View Source
const EggNetwork = "ruust-eggs"

EggNetwork is the shared bridge every Egg container joins. Inter-container communication is disabled on it (enable_icc=false), so two Eggs co-located on the same host cannot reach each other over the internal network, whilst each Egg keeps unmetered outbound egress and its ingress through the published port. One shared network, rather than one per Egg, avoids exhausting Docker's default address pool at scale (a per-Egg network would run out at a few dozen Eggs).

View Source
const LabelPrefix = "ruust.blob"

LabelPrefix is the namespace for every label the agent sets on containers it owns. We only ever enumerate, start, stop or restart containers carrying this prefix.

View Source
const PersistentVolumePrefix = "ruust-vol-"

PersistentVolumePrefix is the naming convention for an Egg's persistent volume (ruust-vol-<blobId>, set by the control plane). We measure only these, not the anonymous volumes an image's own VOLUME instruction creates (a stateless web Egg's image can declare one), so a web Egg reports zero and a database Egg reports the real data size.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client interface {
	// List returns every container carrying our label prefix, running or not.
	List(ctx context.Context) ([]Container, error)
	// EnsureImage pulls the image if it is not already present. Idempotent.
	EnsureImage(ctx context.Context, imageRef string) error
	// Create creates and starts the given replica of a workload with hard limits on
	// every axis. It is idempotent by (workload, replica) identity: if a matching
	// container already exists for this spec version it is a no-op.
	Create(ctx context.Context, spec contract.WorkloadSpec, version string, replica int) (Container, error)
	// Stop stops and removes the container with the given ID immediately. Idempotent:
	// a missing container is treated as success.
	Stop(ctx context.Context, id string) error
	// Drain gracefully stops (SIGTERM, grace period) and removes a container that has
	// been superseded by a healthy replacement, so in-flight requests can finish. Used
	// for the old container in a zero-downtime rolling deploy. Idempotent.
	Drain(ctx context.Context, id string) error
	// Restart restarts the container with the given ID. Idempotent.
	Restart(ctx context.Context, id string) error
	// Stats returns current memory and CPU usage for a container, read via the
	// Docker Engine API so it works uniformly on Linux and on Docker Desktop
	// (macOS, Windows), where container cgroups are not on the host filesystem.
	Stats(ctx context.Context, id string) (contract.CgroupUsage, error)
	// Logs returns container output newer than `since` (an RFC3339 timestamp). If
	// `since` is empty a short tail is returned, so a first report is not a flood.
	// Read via the Docker Engine API so it works on Linux and Docker Desktop alike.
	Logs(ctx context.Context, id string, since string) ([]contract.LogLine, error)
	// TakeReleaseReports returns and clears the release-command outcomes captured since
	// the last call, keyed by workload ID, for the agent to attach to its status report.
	TakeReleaseReports() map[string]*contract.ReleaseReport
	// Close releases any underlying SDK resources.
	Close() error
}

Client is the surface the reconcile loop depends on. It is deliberately narrow and idempotent: List reflects reality, and Ensure/Stop/Restart can be called repeatedly with the same arguments safely.

func NewEngineClient

func NewEngineClient() (Client, error)

NewEngineClient connects to the local Docker daemon using the environment (DOCKER_HOST and friends) and negotiates the API version so the agent tolerates daemon version skew.

type Container

type Container struct {
	// ID is the Docker container ID.
	ID string
	// WorkloadID is the WorkloadSpec.ID this container serves.
	WorkloadID string
	// BlobID is the internal Blob identity of the Egg.
	BlobID string
	// EggID is the customer-facing Egg identity.
	EggID string
	// ImageRef is the image this container was created from.
	ImageRef string
	// SpecVersion is the desired-state version this container was created for.
	SpecVersion string
	// State is the low-level runtime state.
	State contract.ContainerState
	// Healthy reports the last observed health-check result.
	Healthy bool
	// RestartCount is the Docker-reported restart count.
	RestartCount int
	// CgroupPath is the absolute cgroup v2 path for this container, used to read
	// resource stats directly.
	CgroupPath string
	// ReplicaIndex is the 0-based replica index within the workload. A workload
	// running N replicas has containers with indices 0..N-1.
	ReplicaIndex int
	// PublishedPort is the host port this container's app port is published on (0
	// when it is not published, e.g. a database Egg). Ingress load-balances across
	// the published ports of a workload's live replicas.
	PublishedPort int
	// Legacy is true for a container created by an older agent that predates replica
	// indexing (no replica label). It is always rolled so the fleet converges onto
	// the indexed naming.
	Legacy bool
}

Container is the agent's view of one running (or exited) container it owns. It is intentionally a small, SDK-independent shape so the reconcile package never imports the Docker SDK directly.

type DiskUsager added in v0.1.12

type DiskUsager interface {
	VolumeUsageBytes(ctx context.Context, containerID string) (int64, error)
}

DiskUsager measures the on-disk usage of a container's persistent volumes. Kept separate from the core Client so reconcile and its fake need not know about it; the real engineClient implements it and the agent type-asserts to it.

type Migrator added in v0.1.12

type Migrator interface {
	// SnapshotLive streams a logical dump of the LIVE serving container to w and reads
	// the verification counts. The source keeps serving during the dump: pg_dump takes
	// an MVCC-consistent snapshot and redis SAVE flushes current memory to disk, so the
	// dump reflects a real point in time. Snapshotting the live container (rather than a
	// throwaway on a stopped volume) is what makes it correct for an in-memory engine
	// like Redis, whose data is not on disk until it is saved.
	SnapshotLive(ctx context.Context, engine, containerID string, env []string, w io.Writer) (contract.MigrationCounts, error)
	// RestoreDatabase restores a logical dump read from r into a FRESH copy of the
	// Egg's volume, reads the verification counts, then removes the restore container
	// (the Egg only starts serving on the target after cutover). Isolated (no network)
	// and idempotent: a retry re-restores cleanly.
	RestoreDatabase(ctx context.Context, engine, name, volumeName, image string, env []string, r io.Reader) (contract.MigrationCounts, error)
}

Migrator is the database Egg migration capability, kept separate from the core Client so the reconcile logic (and its fake) need not know about it. The real engineClient implements both; the agent's migration handler type-asserts to this.

Jump to

Keyboard shortcuts

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