Documentation
¶
Overview ¶
Package docker implements the Backend interface for Docker container provisioning. It is the production backend bundled with Fred.
For operators and tenants, see the README.md alongside this package (internal/backend/docker/README.md) for the full configuration reference, HTTP API, lease state machine, callback protocol, and Traefik integration.
Architecture overview (for developers) ¶
The package is organized around one main concurrency primitive: the per-lease actor. A lease's actor is created on its first message and serializes that lease's lifecycle commands through a stateless state machine. The actor and SM implementations are substrate-agnostic and live in internal/backend/shared/leasesm; this package supplies the Docker-specific seams via the closure-builder factory in lease_actor_factory.go. Some work is serialized differently: recoverState holds the recovery mutexes across its Docker inventory read, managed volumes are created under per-volume stripe locks, and operation-intent and maintenance recovery replace lease projections directly instead of through an actor.
The actor model is what gives the backend its key properties:
- No held locks during slow I/O (image pull, container create/start)
- Deterministic preemption — a Deprovision arriving mid-provisioning cancels the in-flight worker via OnExit and transitions cleanly
- Blast-radius-contained panics — recover() in each handler keeps unrelated leases unaffected
- One durable terminal result per operation — each result is committed together with its outbox entry in one bbolt transaction under the per-lease journal gate: operation and maintenance results through shared.CallbackPublisher, whether they come from an SM entry action, admission, recovery or maintenance settlement, and close results through shared.CloseSettlement. Delivery then replays until acknowledged
Major components ¶
- internal/backend/shared/leasesm: per-lease actor + state machine (substrate-agnostic; Docker is its only consumer today, since the k3s scaffold deliberately does not use it)
- lease_actor_factory.go, lease_actor_routing.go: factory wiring Docker dependencies into leasesm.NewLeaseActor, plus Backend-side routing/dispatch around the actor inbox (b.actors map, routeToLease, DebugActors)
- leasesm_adapters.go, leasesm_metrics.go: Docker implementations of leasesm.InstanceInspector / DiagnosticsGatherer / LeaseProvisionStore / SMMetrics
- internal/backend/shared/workbarrier: per-actor worker reference counter (used by OnExit to wait for canceled goroutines before completing the transition)
- provision.go, deprovision.go, restart_update.go, restore.go: the lifecycle workers that the actor spawns for each long-running operation
- recover.go: state recovery from Docker labels on startup and on each pass of the backend's own periodic reconcile loop
- compose.go, compose_project.go: Compose-based stack provisioning
- reconcile_custom_domain.go: Traefik label sync for tenant custom domains
- volume.go (+ volume_btrfs.go, volume_xfs.go, volume_zfs.go): filesystem-specific quota enforcement for durable SKU volumes and optional diskless writable-path scratch
- ingress.go: Traefik label generation for routable ports
- metrics.go: Prometheus metrics under fred_docker_backend_*
Container hardening ¶
Every container is created with: dropped capabilities, no-new-privileges, fred's tenant seccomp profile (derived from Docker's default), read-only rootfs, tmpfs for /tmp and /run, PID limits, no swap, restart policy disabled (for crash detection), and per-lease network isolation. See the README for the full list and operator-facing knobs.
Index ¶
- Constants
- Variables
- func ComputeFQDN(subdomain, wildcardDomain string) string
- func ComputeSubdomain(leaseUUID, serviceName string, instanceIndex, quantity int) string
- func CustomDomainRouterName(leaseUUID, serviceName string) string
- func InitRequestKeyMetrics(keys hmacauth.VerifyKeys)
- func InitializeStorageIdentityForConfig(ctx context.Context, cfg Config, logger *slog.Logger, ...) (backendidentity.ID, error)
- func InspectUnsettledDockerEffectsForConfig(ctx context.Context, cfg Config, logger *slog.Logger) (inspection shared.DockerRecoveryInspection, err error)
- func RecordRequestSignatureChecked(slot hmacauth.KeySlot, err error)
- func RecordRequestSignatureMissing()
- func RepairUnsettledDockerEffectsForConfig(ctx context.Context, cfg Config, logger *slog.Logger, ...) (result shared.DockerRecoveryRepairResult, err error)
- func RouterName(leaseUUID, serviceName string, instanceIndex, quantity int) string
- func SelectIngressPort(ports map[string]manifest.PortConfig) (int, bool)
- func TenantNetworkName(tenant string) string
- func TraefikCustomDomainLabels(cfg IngressConfig, customRouterName, customDomain string, containerPort int) map[string]string
- func TraefikLabels(cfg IngressConfig, networkName, routerName, fqdn string, containerPort int) map[string]string
- type ActorSnapshot
- type AdoptionAuditFinding
- type AdoptionAuditRelease
- type AdoptionFindingClass
- type Backend
- func (b *Backend) DebugActors() []ActorSnapshot
- func (b *Backend) Deprovision(ctx context.Context, leaseUUID string) error
- func (b *Backend) GetInfo(ctx context.Context, leaseUUID string) (*backend.LeaseInfo, error)
- func (b *Backend) GetLoadStats(_ context.Context) (*backend.LoadStats, error)
- func (b *Backend) GetLogs(ctx context.Context, leaseUUID string, tail int) (map[string]string, error)
- func (b *Backend) GetProvision(_ context.Context, leaseUUID string) (*backend.ProvisionInfo, error)
- func (b *Backend) GetReleases(_ context.Context, leaseUUID string) ([]backend.ReleaseInfo, error)
- func (b *Backend) Health(ctx context.Context) error
- func (b *Backend) InitializeStorageIdentity(ctx context.Context) error
- func (b *Backend) ListProvisions(_ context.Context) ([]backend.ProvisionInfo, error)
- func (b *Backend) ListProvisionsPage(ctx context.Context, after string, limit int) ([]backend.ProvisionInfo, string, error)
- func (b *Backend) ListRetentions(_ context.Context) ([]backend.RetainedLease, error)
- func (b *Backend) ListRetentionsPage(_ context.Context, after string, limit int) ([]backend.RetainedLease, string, error)
- func (b *Backend) LookupProvisions(_ context.Context, uuids []string) ([]backend.ProvisionInfo, error)
- func (b *Backend) Name() string
- func (b *Backend) Provision(ctx context.Context, request backend.ProvisionRequest) error
- func (b *Backend) ReconcileCustomDomain(ctx context.Context, leaseUUID string, items []backend.LeaseItem) error
- func (b *Backend) RefreshState(ctx context.Context) error
- func (b *Backend) Restart(ctx context.Context, req backend.RestartRequest) error
- func (b *Backend) Restore(ctx context.Context, request backend.RestoreRequest) error
- func (b *Backend) RuntimeRefreshStatus(ctx context.Context) (RuntimeRefreshStatus, error)
- func (b *Backend) Start(ctx context.Context) error
- func (b *Backend) StartRuntimeRefresh(ctx context.Context) (RuntimeRefreshStatus, error)
- func (b *Backend) Stats() shared.ResourceStats
- func (b *Backend) Stop() error
- func (b *Backend) StopContext(ctx context.Context) error
- func (b *Backend) StorageIdentity() backendidentity.ID
- func (b *Backend) TerminalStorageAuthorityFailure() <-chan error
- func (b *Backend) Update(ctx context.Context, req backend.UpdateRequest) error
- func (b *Backend) VerifyStorageIdentity(ctx context.Context) error
- type Config
- func (c *Config) GetHostBindIP() string
- func (c *Config) GetMinAvgFileBytes() int64
- func (c *Config) GetPidsLimit() *int64
- func (c *Config) GetRuntimeRefreshMaxConcurrency() int
- func (c *Config) GetSKUProfile(sku string) (SKUProfile, error)
- func (c *Config) GetTmpfsSizeMB() int
- func (c *Config) HasStatefulSKUs() bool
- func (c *Config) IsNetworkIsolation() bool
- func (c *Config) IsReadonlyRootfs() bool
- func (c *Config) RequestKeys() (hmacauth.VerifyKeys, error)
- func (c *Config) Validate() error
- type ContainerEvent
- type ContainerInfo
- type ContainerMount
- type CreateContainerParams
- type DaemonSecurityInfo
- type DockerClient
- func (d *DockerClient) AdmitImage(ctx context.Context, reference string) (imageexec.Image, error)
- func (d *DockerClient) Close() error
- func (d *DockerClient) ContainerEvents(ctx context.Context) (<-chan ContainerEvent, <-chan error)
- func (d *DockerClient) ContainerLogs(ctx context.Context, containerID string, tail int) (string, error)
- func (d *DockerClient) CreateContainer(ctx context.Context, params CreateContainerParams, timeout time.Duration) (string, error)
- func (d *DockerClient) CreateLeaseNetwork(ctx context.Context, leaseUUID string) (leaseNetworkAuthority, error)
- func (d *DockerClient) DaemonInfo(ctx context.Context) (DaemonSecurityInfo, error)
- func (d *DockerClient) DetectVolumeOwner(ctx context.Context, imageName imageexec.Image, volumePaths []string, ...) (uid, gid int, err error)
- func (d *DockerClient) DetectWritablePaths(ctx context.Context, imageName imageexec.Image, uid int, ...) (paths []string, err error)
- func (d *DockerClient) ExtractImageContent(ctx context.Context, imageName imageexec.Image, paths []string, destDir string, ...) (failures map[string]error)
- func (d *DockerClient) InspectContainer(ctx context.Context, containerID string) (*ContainerInfo, error)
- func (d *DockerClient) InspectLeaseNetwork(ctx context.Context, leaseUUID string) (authority leaseNetworkAuthority, present bool, err error)
- func (d *DockerClient) ListIdleManagedNetworks(ctx context.Context) ([]networktypes.Inspect, error)
- func (d *DockerClient) ListManagedContainers(ctx context.Context) ([]ContainerInfo, error)
- func (d *DockerClient) ListManagedContainersStrict(ctx context.Context) ([]ContainerInfo, error)
- func (d *DockerClient) ListVolumeWriters(ctx context.Context) ([]ContainerInfo, error)
- func (d *DockerClient) Ping(ctx context.Context) error
- func (d *DockerClient) RemoveContainer(ctx context.Context, containerID string) error
- func (d *DockerClient) RemoveLeaseNetworkIfEmpty(ctx context.Context, authority leaseNetworkAuthority) (networkRemoval, error)
- func (d *DockerClient) RemoveTenantNetworkIfEmpty(ctx context.Context, tenant string) (networkRemoval, error)
- func (d *DockerClient) RenameContainer(ctx context.Context, containerID string, newName string) error
- func (d *DockerClient) RequireImage(ctx context.Context, imageName string) error
- func (d *DockerClient) ResolveImageUser(ctx context.Context, imageName imageexec.Image, userOverride string, ...) (uid, gid int, err error)
- func (d *DockerClient) StartContainer(ctx context.Context, containerID string, timeout time.Duration) error
- func (d *DockerClient) StopContainer(ctx context.Context, containerID string, timeout time.Duration) error
- func (d *DockerClient) TenantSeccompCensus(ctx context.Context) (tenantSeccompCensus, error)
- type HealthStatus
- type IngressConfig
- type PortBinding
- type RetentionTenantBudget
- type RuntimeRefreshLease
- type RuntimeRefreshOutcome
- type RuntimeRefreshRestored
- type RuntimeRefreshStatus
- type SKUProfile
- type StoppedDrainReport
- type StorageIdentityAdoptionAudit
- type StorageIdentityAdoptionVerdict
- type StorageIdentityInitializationMode
- type TenantQuotaConfig
Constants ¶
const ( LabelManaged = "fred.managed" LabelLeaseUUID = "fred.lease_uuid" LabelTenant = "fred.tenant" LabelProviderUUID = "fred.provider_uuid" LabelSKU = "fred.sku" LabelCreatedAt = "fred.created_at" LabelInstanceIndex = "fred.instance_index" LabelFailCount = "fred.fail_count" LabelCallbackURL = "fred.callback_url" LabelLifecycleCallbackURL = "fred.lifecycle_callback_url" LabelMaintenanceID = "fred.maintenance_id" LabelBackendName = "fred.backend_name" LabelServiceName = "fred.service_name" LabelFQDN = "fred.fqdn" LabelCustomDomain = "fred.custom_domain" // LabelNetworkScope marks a network Fred owns per lease. Legacy per-tenant // networks carry LabelTenant instead. LabelNetworkScope = "fred.network_scope" // LabelRuntimeContract records the runtime contract a container was created // under (see currentRuntimeContract). A container without it predates the // contract. LabelRuntimeContract = "fred.runtime_contract" LabelImageReference = imageexec.LabelImageReference LabelImageID = imageexec.LabelImageID )
Labels used for tracking managed containers.
const DefaultMaxRequestBodySize int64 = 2 << 20 // 2 MiB
DefaultMaxRequestBodySize caps inbound HTTP request bodies for the docker backend. It is deliberately larger than providerd's config.DefaultMaxRequestBodySize (1 MiB): providerd caps the raw tenant body, then re-serializes and wraps it (JSON envelope + base64) before forwarding, so a manifest that just cleared providerd can exceed 1 MiB on the backend hop and would otherwise be rejected with an opaque 400. Configurable via max_request_body_size / DOCKER_BACKEND_MAX_REQUEST_BODY_SIZE. (ENG-448 / F42)
Variables ¶
var ( // ErrRuntimeRefreshDisabled reports that runtime_refresh_max_concurrency is 0. ErrRuntimeRefreshDisabled = errors.New("runtime refresh is disabled (runtime_refresh_max_concurrency is 0)") // ErrRuntimeRefreshActive reports that a run is already active; a new run // can start once it has finished. ErrRuntimeRefreshActive = errors.New("a runtime refresh run is already active") )
var ErrPreStackWorkloadUnsupported = errors.New("pre-stack Docker workload is unsupported")
ErrPreStackWorkloadUnsupported identifies the old single-service Docker layout which predates fred's Compose stack model. Production inventory was verified to contain no such workloads before this migration was removed. The current binary deliberately refuses the shape instead of inferring missing topology or performing an unjournaled in-place conversion.
var ErrShutdownDrainTimeout = errors.New("docker backend workers did not drain before shutdown deadline")
ErrShutdownDrainTimeout means backend-owned goroutines did not quiesce by the shutdown safety deadline. Stop deliberately leaves the Docker client and durable stores open in this case because a late worker may still use them. The serving binary treats this as a forced, non-zero process exit; embedded callers may retry Stop only after they can prove the workers have drained.
var ErrV013InterruptedDeprovision = errors.New("v0.13 lineage contains an interrupted deprovision finalization")
ErrV013InterruptedDeprovision identifies a stopped legacy lineage that must finish its exact close operation under v0.13 before it can be sealed.
var ErrV013UnresolvedClose = errors.New("v0.13 lineage contains an unresolved close boundary")
ErrV013UnresolvedClose identifies a stopped legacy lineage whose active release outlived its container cohort without a retention finalizer. That shape is compatible with more than one v0.13 close boundary, so neither replay nor automatic journal repair is safe.
var ErrVolumeDeleteHeld = errors.New("volume delete held")
ErrVolumeDeleteHeld means a volume's deletion could not finish for a reason confined to that one volume, and the manager holds it. The delete stage and the volume's reserved project ID are kept, so are its quota limits unless the deletion had already reached them, the name cannot be created again, and the manager's hold executor retries the deletion in the background. It never stops the Backend: a caller keeps its own durable authority, exactly as for any failed destroy, and retries later. (ENG-1117)
var ErrVolumeMutationRecoveryPending = errors.New("volume mutation recovery pending")
ErrVolumeMutationRecoveryPending means a volume manager met an authority contradiction or an ambiguous outcome while consuming durable mutation evidence: a project-ID, stage or marker authority that disagrees with the manager's records, or a namespace change whose durability is unknown. Only those classes carry it. The mutation adapter fail-stops the live Backend on it, and a fresh process must run startup recovery. A failure confined to one volume's deletion is ErrVolumeDeleteHeld instead and never stops the Backend.
Functions ¶
func ComputeFQDN ¶
ComputeFQDN returns subdomain + "." + wildcardDomain.
func ComputeSubdomain ¶
ComputeSubdomain derives a unique, human-friendly subdomain from lease and service metadata. The result is guaranteed to be at most 63 characters (the DNS label limit); long service names are truncated to fit.
The hash suffix is derived from all discriminating fields (leaseUUID, serviceName, instanceIndex) to prevent cross-pattern collisions — e.g., service "web" instance 0 vs. service "web-0" instance 0.
Matrix:
serviceName == "" && quantity <= 1 → {hash7}
serviceName == "" && quantity > 1 → {idx}-{hash7}
serviceName != "" && quantity <= 1 → {svc}-{hash7}
serviceName != "" && quantity > 1 → {svc}-{idx}-{hash7}
func CustomDomainRouterName ¶
CustomDomainRouterName returns a Traefik router name shared across all instances of (leaseUUID, serviceName). Custom domain is per-LeaseItem (per-service, not per-instance), so this name is the same regardless of instance index — every instance container of the service emits the same secondary-router labels and Traefik aggregates them into one router with N backends. The "-custom" suffix avoids any collision with the per-instance primary router name produced by RouterName.
func InitRequestKeyMetrics ¶ added in v0.14.0
func InitRequestKeyMetrics(keys hmacauth.VerifyKeys)
InitRequestKeyMetrics pre-creates the request key series for keys: the current slot always, the next slot only while a next key is configured, so a zero on "next" means unused rather than not configured.
func InitializeStorageIdentityForConfig ¶ added in v0.14.0
func InitializeStorageIdentityForConfig( ctx context.Context, cfg Config, logger *slog.Logger, mode StorageIdentityInitializationMode, ) (backendidentity.ID, error)
InitializeStorageIdentityForConfig is the explicit, one-shot storage-lineage sealing operation. It validates and pins the Docker substrate, binds the callback, release, and retention journals to one crash-resumable marker pair, and closes every temporary handle. The diagnostics database is deliberately outside this authority set. Normal New/Start paths are verify-only and refuse an absent, incomplete, or foreign member.
func InspectUnsettledDockerEffectsForConfig ¶ added in v0.14.0
func InspectUnsettledDockerEffectsForConfig(ctx context.Context, cfg Config, logger *slog.Logger) (inspection shared.DockerRecoveryInspection, err error)
InspectUnsettledDockerEffectsForConfig reports the exact stopped-journal snapshot and operator fencing statement needed for exceptional recovery.
func RecordRequestSignatureChecked ¶ added in v0.14.0
RecordRequestSignatureChecked counts one providerd request signature check: the verifying key slot on success, or the closed refusal reason.
func RecordRequestSignatureMissing ¶ added in v0.14.0
func RecordRequestSignatureMissing()
RecordRequestSignatureMissing counts a providerd request with no signature.
func RepairUnsettledDockerEffectsForConfig ¶ added in v0.14.0
func RepairUnsettledDockerEffectsForConfig(ctx context.Context, cfg Config, logger *slog.Logger, acknowledgement, backupPath string) (result shared.DockerRecoveryRepairResult, err error)
RepairUnsettledDockerEffectsForConfig is an offline operator workflow. It does not stop Docker or infer a fence from inventory. Its exact acknowledgement attests that old clients and daemon/runtime requests have already been fenced.
func RouterName ¶
RouterName returns a router name derived from lease metadata.
func SelectIngressPort ¶
func SelectIngressPort(ports map[string]manifest.PortConfig) (int, bool)
SelectIngressPort picks the best TCP port for ingress routing. Preference: ingress hint > 80 > 8080 > lowest TCP port number. Returns (port, true) if a suitable port is found, (0, false) otherwise.
func TenantNetworkName ¶
TenantNetworkName returns the deterministic name of the per-tenant network that releases before per-lease networks attached every lease of a tenant to. Fred no longer creates it; the network reclamation worker removes it once the tenant has no provisions on this backend.
func TraefikCustomDomainLabels ¶
func TraefikCustomDomainLabels(cfg IngressConfig, customRouterName, customDomain string, containerPort int) map[string]string
TraefikCustomDomainLabels generates the secondary Traefik labels that route a tenant-supplied custom domain to the same per-tenant container(s) served by the primary router. The returned map is intended to be merged into a container's label set alongside the primary TraefikLabels output.
Multi-instance services (quantity > 1) are load-balanced by emitting these byte-identical labels on every instance container: Traefik's Docker provider deduplicates the router by name and aggregates the service into one with N backends. For single-instance services this is the same path with a degenerate single backend.
The router uses cfg.CustomDomainCertResolver (default "http01") for per-domain ACME and cfg.CustomDomainMiddlewares (default "security-headers@file") for transport-level hardening. The entrypoint reuses the existing IngressConfig.Entrypoint so the secondary router shares the primary's TLS termination posture.
func TraefikLabels ¶
func TraefikLabels(cfg IngressConfig, networkName, routerName, fqdn string, containerPort int) map[string]string
TraefikLabels generates the Docker labels that Traefik uses for auto-discovery and routing configuration. networkName is the lease's Docker network that Traefik should use to reach the container (set via traefik.docker.network). This is the only Traefik-specific function; everything else is proxy-agnostic.
Routers are emitted with tls=true and no certresolver; see IngressConfig for the wildcard-cert provisioning contract.
The explicit `.service` label binding is required even though Traefik's docker provider can auto-bind a router to a same-named service: auto-binding only fires when exactly one service is declared on the container. As soon as a second service appears (notably TraefikCustomDomainLabels' secondary service for tenant custom domains), auto-binding becomes ambiguous and Traefik leaves the router orphaned (returns HTTP 418 on the primary URL). Always emitting the explicit binding is cheap and immune to future label additions.
Types ¶
type ActorSnapshot ¶
type ActorSnapshot struct {
LeaseUUID string `json:"lease_uuid"`
SMState string `json:"sm_state"` // current SM state
InboxDepth int `json:"inbox_depth"` // pending messages not yet processed
InboxCap int `json:"inbox_cap"`
}
ActorSnapshot is a point-in-time view of one lease actor's state for operator introspection. Safe to marshal to JSON for a /debug/actors endpoint when integrated with the HTTP layer.
type AdoptionAuditFinding ¶ added in v0.14.0
type AdoptionAuditFinding struct {
Class string `json:"class"`
Remedy string `json:"remedy"`
LeaseUUID string `json:"lease_uuid,omitempty"`
Subject string `json:"subject,omitempty"`
Message string `json:"message"`
}
AdoptionAuditFinding is one refused shape.
type AdoptionAuditRelease ¶ added in v0.14.0
type AdoptionAuditRelease struct {
LeaseUUID string `json:"lease_uuid"`
Items []backend.LeaseItem `json:"items"`
}
AdoptionAuditRelease carries the v0.13 items adoption would freeze for one active release, so a caller can compare them with chain state at a pinned height.
type AdoptionFindingClass ¶ added in v0.14.0
type AdoptionFindingClass uint8
AdoptionFindingClass is the closed set of v0.13 store shapes that storage identity adoption refuses.
const ( AdoptionFindingUpgradedCallbackStore AdoptionFindingClass AdoptionFindingPendingCallbacks AdoptionFindingIncompleteJournals AdoptionFindingCallbackCohort AdoptionFindingContainerWithoutAuthority AdoptionFindingReleaseRetentionComparison AdoptionFindingInterruptedDeprovision AdoptionFindingUnresolvedClose AdoptionFindingReleaseItems AdoptionFindingReleaseSKU AdoptionFindingRuntimeAuthority AdoptionFindingReleaseCapacity AdoptionFindingContainerVolume AdoptionFindingRetentionIdentity AdoptionFindingRetentionSKU AdoptionFindingRetentionVolume AdoptionFindingReapingVolume AdoptionFindingNonEmptySubstrate AdoptionFindingEmptySubstrate AdoptionFindingMissingEvidenceVolume AdoptionFindingUnexplainedVolume AdoptionFindingLeaseWithoutRelease AdoptionFindingReleaseWithoutCohort )
func (AdoptionFindingClass) Remedy ¶ added in v0.14.0
func (class AdoptionFindingClass) Remedy() string
Remedy names the operator action each finding's refusal text already prescribes. It is advisory: "investigate" means no single remedy is safe to automate.
func (AdoptionFindingClass) String ¶ added in v0.14.0
func (class AdoptionFindingClass) String() string
type Backend ¶
type Backend struct {
// contains filtered or unexported fields
}
Backend implements the backend.Backend interface for Docker containers.
func New ¶
New creates a production Docker backend only after the configured Docker daemon, volume root, and durable marker have been identity-attested. No callback/diagnostic/release/retention database is opened before that proof.
func NewWithContext ¶ added in v0.14.0
NewWithContext is New with a caller-owned deadline for substrate attestation. The context is not retained after construction.
func (*Backend) DebugActors ¶
func (b *Backend) DebugActors() []ActorSnapshot
DebugActors returns a snapshot of every live lease actor. The result is stable for the caller: it's a copy; the registry may grow or change state after return. Intended for ops introspection during incidents — pair with a /debug/actors HTTP handler that JSON-encodes the return.
func (*Backend) Deprovision ¶
Deprovision is the public shim: it routes the request through the lease's actor so that container-death and deprovision messages serialize per lease. Routing forces a Ready/Failing/Failed → Deprovisioning SM transition whose Failing.OnExit cancels the in-flight diag goroutine — the structural suppression of stale Failed callbacks.
func (*Backend) GetInfo ¶
GetInfo returns lease information including connection details. Always populates `Services` keyed by service name (the primary post-Tasks-3-9 source of truth). `Instances` is a flattened convenience view computed by concatenating service instances in deterministic service-name order so older tooling that consumes the flat array keeps working.
func (*Backend) GetLoadStats ¶ added in v0.5.0
GetLoadStats returns the backend's current CPU-load snapshot for least-loaded provision routing (ENG-318). It wraps the resource pool's Stats() — the same source the HTTP GET /stats endpoint serves — so the in-process docker backend exposes the same load signal as the HTTP path.
func (*Backend) GetLogs ¶
func (b *Backend) GetLogs(ctx context.Context, leaseUUID string, tail int) (map[string]string, error)
GetLogs returns the last N lines of stdout/stderr for each container in a lease, keyed by "serviceName/instanceIndex" (e.g., "web/0", "db/0"). Falls back to the diagnostics store when the provision is not in memory (e.g., after deprovision). Returns ErrNotProvisioned only if both miss. On partial failure (some containers succeed, some fail), the successful logs are returned along with error placeholders, and the errors are logged.
Post-Tasks-3-9 the live path always populates `prov.ServiceContainers`, so the legacy "key by instance index only" branch is gone — every lease is stack-shaped from the in-memory state's perspective.
func (*Backend) GetProvision ¶
GetProvision returns a single provision by lease UUID.
When the lease is not in the in-memory map (e.g., after close/expire), the retention store is consulted BEFORE the diagnostics fallback so a soft-deleted lease surfaces as Status=retained with Items for the restore shape and RetainedUntil when age-based reaping is enabled, so an offline tenant can self-serve while data remains retained. It never regresses to a stale Status=failed diagnostics entry. Falls back to the diagnostics store otherwise. Returns ErrNotProvisioned only if all sources miss.
func (*Backend) GetReleases ¶
GetReleases returns the release history for a lease.
func (*Backend) Health ¶
Health checks that the Docker daemon is reachable AND the persistence stores are readable. Probing the bbolt stores (not just docker.Ping) means a locked/corrupt/read-only retention or release store surfaces as unhealthy instead of the backend reporting healthy while soft-delete/restore silently fail — the most data-loss-sensitive subsystem must not be the unmonitored one. (ENG-448 / F31)
func (*Backend) InitializeStorageIdentity ¶ added in v0.14.0
InitializeStorageIdentity loads an already sealed physical-storage identity. It never creates or repairs marker state. New installations and the first upgraded start must run docker-backend's explicit one-shot identity initialization mode before normal construction opens any durable store.
func (*Backend) ListProvisions ¶
ListProvisions returns all currently provisioned resources.
func (*Backend) ListProvisionsPage ¶ added in v0.7.0
func (b *Backend) ListProvisionsPage(ctx context.Context, after string, limit int) ([]backend.ProvisionInfo, string, error)
ListProvisionsPage returns one keyset page of provisions for the /provisions handler — the same paged-handler role ListRetentionsPage plays for /retentions, but NOT the same performance profile. The docker provision store is an in-memory map with no ordered index, so this snapshots it via ListProvisions and paginates in memory (O(N log N) per page, no disk I/O), whereas ListRetentionsPage serves O(limit) pages directly from the ordered bbolt index via a cursor seek. A true store-level O(limit) provision read is tracked as ENG-455 (it needs the ENG-381 ordered snapshot, since recoverState rebuilds the whole map each tick).
func (*Backend) ListRetentions ¶ added in v0.5.0
ListRetentions returns the leases this docker backend currently retains (soft-deleted, awaiting restore or grace-reap), read from the retention store. Used by fred's reconciler for restore backend affinity (ENG-333). Returns an empty slice when the retention store is not configured.
func (*Backend) ListRetentionsPage ¶ added in v0.7.0
func (b *Backend) ListRetentionsPage(_ context.Context, after string, limit int) ([]backend.RetainedLease, string, error)
ListRetentionsPage returns one keyset page of retained lease UUIDs, served directly from the retention store's ordered bbolt index via a cursor Seek (O(limit) per page) rather than reading the whole set and paginating in memory. It is the paged sibling of ListRetentions used by the /retentions handler. limit is coerced down to backend.MaxPageLimit, mirroring PaginateRetentions; limit <= 0 is the unpaginated passthrough.
func (*Backend) LookupProvisions ¶
func (b *Backend) LookupProvisions(_ context.Context, uuids []string) ([]backend.ProvisionInfo, error)
LookupProvisions returns provision info for the requested lease UUIDs. Missing leases are absent from the returned slice (not an error). O(k) lookups against the in-memory provisions map, where k = len(uuids).
func (*Backend) Provision ¶
Provision starts async provisioning of containers. For multi-unit leases (quantity > 1), multiple containers are created. For multi-SKU leases, containers are created with the appropriate profile for each SKU.
Pre-flight validation errors (unknown SKU, invalid manifest, disallowed image, insufficient resources) are returned synchronously so the caller can respond with an appropriate HTTP status. Only truly asynchronous failures (image pull, container create/start) are communicated via callback.
func (*Backend) ReconcileCustomDomain ¶
func (b *Backend) ReconcileCustomDomain(ctx context.Context, leaseUUID string, items []backend.LeaseItem) error
ReconcileCustomDomain reapplies the per-LeaseItem custom_domain values from chain onto the running provision. When at least one item's CustomDomain differs from the in-memory state, the backend computes the diff read-only, then routes the redeploy through the lease actor via routeReplaceRestart. The actor commits prov.Items on success — no off-actor mutation, no CAS rollback. A failed redeploy leaves prov.Items untouched so the next reconciler tick retries (ENG-278).
Reconciliation only runs when the provision is in ProvisionStatusReady. Any other state (Provisioning, Restarting, Updating, Failing, Failed, Deprovisioning, Unknown) is treated as "not the right time": skip without error and let the periodic reconciler call back when the provision settles.
The reconcile uses a two-RLock-pass candidate-only shape (ENG-277): a candidate pre-pass (RLock) finds which incoming domains actually differ from what's emitted and need DNS resolution; only those are resolved off-lock via b.dnsGateAllows; a main pass (RLock) re-reads fresh prov and runs computeCustomDomainOverrides read-only. A steady-state lease performs zero DNS lookups; a not-Ready lease short-circuits in the pre-pass with zero DNS.
No-op when this backend is configured with ingress disabled. Without ingress, applyIngressLabels emits no Traefik labels (primary or secondary) at all, so a Restart triggered for custom-domain drift would recreate the containers with no LabelCustomDomain — and the next recoverState tick would rebuild prov.Items[].CustomDomain back to "" from the unlabeled containers, putting the reconciler into a permanent restart loop against the chain's non-empty value. Returning early here avoids the loop.
func (*Backend) RefreshState ¶
RefreshState synchronizes in-memory provision state with Docker.
func (*Backend) Restart ¶
Restart restarts containers for a lease without changing the manifest. State machine: Ready|Failed → Restarting → Ready|Failed
SEAM CLOSED (ENG-230). This prelude is read-only: it fast-fails on ErrNotProvisioned / ErrInvalidState under provisionsMu, snapshots the fields the worker needs, then does pure work (manifest marshal + release-store Append). It performs NO write to prov.Status or either callback URL. The lease actor's onEnterRestarting entry action is the sole writer of prov.Status, firing inside handleRestartRequested BEFORE the ack; a moved callback route stays pending actor state until the target generation activates. Because Restart() returns only after observing that ack, the "Restart() returns => prov.Status == Restarting" invariant the HTTP handler's event-broker publish depends on (api/handlers.go: RestartLease) is preserved without an off-actor write.
The prelude's fast-fail is only a route-time precondition — it does NOT guarantee the lease is still Ready/Failed when the actor dequeues the message. The real serialization is the actor inbox (the only path that mutates prov.Status). So a same-lease concurrent restart that passes the route-time check but loses the race (the winner already ran onEnterRestarting) is REJECTED by the actor, not prevented here: handleRestartRequested's classifyReplaceReject returns ErrInvalidState for the busy SM, which this function forwards and api/handlers.go maps to a clean 409.
Durable admission owns the target before actor dispatch. Only a proven no-enqueue or explicit actor rejection can settle pre-effect refusal. A lost reply preserves that exact head for replay and bounded recovery.
func (*Backend) Restore ¶ added in v0.5.0
Restore adopts a soft-deleted lease's retained volumes into a NEW lease and brings up its stack from the retained manifest (ENG-325). The new lease is reserved at Provisioning and driven through the existing replace machinery via evRestoreRequested (Provisioning→Restarting→Ready|Failed).
Ordering is load-bearing:
(a) validate against the retained record (read-only), (b) under the recovery snapshot fence, commit the exact operation intent, (c) reserve the new-lease provision and allocate pool slots, (d) ATOMICALLY claim active→restoring (closes the prelude-vs-reaper race), (e) hand off to the actor before any substrate mutation, (f) the Started executor adopts the volumes and brings up the stack.
Construction-bound settlement owns terminal outcomes. An ambiguous worker result preserves the exact Started intent, restoring finalizer, and live reservation until operation recovery proves completion or rolls back adoption.
Synchronous errors (validation, already-provisioned, insufficient resources, not-retained, not-restorable) are returned to the caller; asynchronous outcomes flow via the lease callback.
func (*Backend) RuntimeRefreshStatus ¶ added in v0.14.0
func (b *Backend) RuntimeRefreshStatus(ctx context.Context) (RuntimeRefreshStatus, error)
RuntimeRefreshStatus reports what Docker shows now, every lease's durable refresh outcome, and the current or most recent run. A Docker inventory or receipt read error is returned, never reported as zero.
func (*Backend) StartRuntimeRefresh ¶ added in v0.14.0
func (b *Backend) StartRuntimeRefresh(ctx context.Context) (RuntimeRefreshStatus, error)
StartRuntimeRefresh starts a run and returns the resulting status. It refuses with ErrRuntimeRefreshActive while a run is active. The run belongs to the backend's lifetime, never to the request that started it.
func (*Backend) Stats ¶
func (b *Backend) Stats() shared.ResourceStats
Stats returns current resource usage statistics.
func (*Backend) StopContext ¶ added in v0.14.0
StopContext shares the caller's shutdown deadline with admitted imports and worker drain. The process can budget HTTP and backend shutdown together; direct Stop callers retain the ordinary backend drain limit.
func (*Backend) StorageIdentity ¶ added in v0.14.0
func (b *Backend) StorageIdentity() backendidentity.ID
StorageIdentity returns the initialized immutable storage identity. Its zero value means production initialization has not completed.
func (*Backend) TerminalStorageAuthorityFailure ¶ added in v0.14.0
TerminalStorageAuthorityFailure reports the first backend-lifetime storage failure that requires a fresh process to re-open durable stores and run startup recovery. The receive-only channel yields at most one error and is never closed. A nil channel means this Backend was assembled by an isolated test rather than the production constructor and therefore disables the corresponding select case.
func (*Backend) Update ¶
Update deploys a new manifest for a lease, replacing containers. State machine: Ready|Failed → Updating → Ready|Failed
SEAM CLOSED (ENG-230) — see the extended comment on Backend.Restart. Like Restart, the prelude is read-only: it fast-fails / validates under provisionsMu, snapshots fields, then records the release. It performs NO write to prov.Status or either callback URL — the actor's onEnterUpdating entry action is the sole status writer, firing inside handleUpdateRequested BEFORE the ack, so the "Update() returns => Status is Updating" contract holds without an off-actor write. No rollback is needed on any failure path (nothing on prov was mutated).
func (*Backend) VerifyStorageIdentity ¶ added in v0.14.0
VerifyStorageIdentity re-attests the marker and current Docker daemon before any request can observe or mutate backend state. It never creates a missing marker, so a runtime remount/replacement fails closed.
type Config ¶
type Config struct {
// LogLevel controls the log verbosity (debug, info, warn, error).
// When empty, defaults to "info" at startup (see cmd/docker-backend/main.go).
LogLevel string `yaml:"log_level"`
// Name is the backend identifier.
Name string `yaml:"name"`
// ListenAddr is the address the HTTP server listens on.
ListenAddr string `yaml:"listen_addr"`
// MaxRequestBodySize caps inbound HTTP request bodies (bytes). It must
// exceed providerd's request cap plus forward-wrapping overhead; defaults to
// DefaultMaxRequestBodySize when unset or non-positive. (ENG-448 / F42)
MaxRequestBodySize int64 `yaml:"max_request_body_size"`
// ProductionMode tightens startup checks beyond basic validation. When true,
// Validate requires a local Unix Docker socket and rejects dev-only insecure
// toggles such as callback_insecure_skip_verify, which disables TLS verification on the
// backend → Fred callback hop. Mirrors providerd's production_mode (which
// gates the reverse providerd → backend tls_skip_verify). Defaults to false.
ProductionMode bool `yaml:"production_mode"`
// TLSCertFile and TLSKeyFile enable HTTPS on the listener when both are
// set; otherwise it serves plaintext HTTP (the default). Loaded once at
// startup — rotation requires a restart (see ENG-294).
TLSCertFile string `yaml:"tls_cert_file"`
TLSKeyFile string `yaml:"tls_key_file"`
// TLSClientCAFile turns on mutual TLS when set: the listener requires and
// verifies a client certificate signed by this CA. Requires TLSCertFile and
// TLSKeyFile (the listener must be on TLS first).
TLSClientCAFile string `yaml:"tls_client_ca_file"`
// TLSClientAllowedNames optionally pins the mTLS client's identity: the
// presented certificate's CommonName or a DNS SAN must be in this list.
// Empty accepts any certificate signed by TLSClientCAFile. Requires
// TLSClientCAFile. Use this whenever the client CA is not dedicated solely
// to providerd.
TLSClientAllowedNames []string `yaml:"tls_client_allowed_names"`
// DockerHost is the Docker daemon socket path or URL.
DockerHost string `yaml:"docker_host"`
// TotalCPUCores is the total CPU cores available in the resource pool.
TotalCPUCores float64 `yaml:"total_cpu_cores"`
// TotalMemoryMB is the total memory available in MB.
TotalMemoryMB int64 `yaml:"total_memory_mb"`
// TotalDiskMB is the physical disk admission pool in MB. Live Docker
// instances charge either durable SKU disk or their pinned diskless scratch
// allowance; retained/reaping durable footprints share the same pool.
TotalDiskMB int64 `yaml:"total_disk_mb"`
// SKUMapping maps on-chain SKU UUIDs to profile names.
// This allows the backend to translate chain SKU UUIDs to local resource profiles.
// Example: {"019c1ee7-1aaf-7000-802c-ad775c72cc27": "docker-small"}
SKUMapping map[string]string `yaml:"sku_mapping"`
// SKUProfiles maps SKU names to resource profiles.
SKUProfiles map[string]SKUProfile `yaml:"sku_profiles"`
// AllowedRegistries is the list of allowed container registries.
AllowedRegistries []string `yaml:"allowed_registries"`
// CallbackSecret is the HMAC secret for signing callbacks.
CallbackSecret config.Secret `yaml:"callback_secret"`
// CallbackSecretNext is a verify-only key accepted on providerd requests
// during a key rotation, before providerd switches to it. The backend
// never signs with it. See DEPLOYMENT.md, "Rotating a backend's HMAC key".
CallbackSecretNext config.RotationSecret `yaml:"callback_secret_next"`
// HostAddress is the external address for port mappings.
HostAddress string `yaml:"host_address"`
// ImagePullTimeout is the timeout for pulling images.
ImagePullTimeout time.Duration `yaml:"image_pull_timeout"`
// ImageMaxSizeMB bounds staged content and expanded image bytes before
// Docker import, and the inspected image size afterward.
ImageMaxSizeMB int64 `yaml:"image_max_size_mb"`
// ImageDataPath declares the daemon's actual image content directory. It is
// required for containerd snapshotters, whose store can be outside DockerRootDir.
// It may share a filesystem with journals or reside apart from DockerRootDir.
ImageDataPath string `yaml:"image_data_path"`
// ImageDiskMinFreeMB is a sampled free-space floor, not a physical reservation.
// Staging also requires ImageMaxSizeMB; import rechecks the verified content's
// conservative footprint. Launches require the floor. Zero selects 2048 MiB.
ImageDiskMinFreeMB int64 `yaml:"image_disk_min_free_mb"`
// ImageGCHighPercent triggers collection of unused, unpinned images;
// ImageGCLowPercent is its stop threshold. Zero selects 85/75 percent.
ImageGCHighPercent int `yaml:"image_gc_high_percent"`
ImageGCLowPercent int `yaml:"image_gc_low_percent"`
// StorageAttestationTimeout bounds the full startup storage inventory proof,
// including all per-volume substrate checks. Zero selects 30s. Large fleets
// can widen this without changing per-request Docker or provisioning limits.
StorageAttestationTimeout time.Duration `yaml:"storage_attestation_timeout"`
// ContainerCreateTimeout is the timeout for creating containers.
ContainerCreateTimeout time.Duration `yaml:"container_create_timeout"`
// ContainerStartTimeout is the timeout for starting containers.
ContainerStartTimeout time.Duration `yaml:"container_start_timeout"`
// ContainerStopTimeout is the grace period for stopping containers.
// Containers receive SIGTERM and have this long to shut down gracefully
// before being force-killed (SIGKILL). Defaults to 30 seconds.
ContainerStopTimeout time.Duration `yaml:"container_stop_timeout"`
// ReconcileInterval is how often to reconcile state with Docker.
ReconcileInterval time.Duration `yaml:"reconcile_interval"`
// CallbackInsecureSkipVerify skips TLS certificate verification for callbacks.
// WARNING: This disables TLS certificate validation, enabling MITM attacks.
// NEVER enable in production. Only use for local development with self-signed certificates.
CallbackInsecureSkipVerify bool `yaml:"callback_insecure_skip_verify"`
// CallbackDBPath is the path to the bbolt database for persisting pending callbacks.
// Defaults to "callbacks.db".
CallbackDBPath string `yaml:"callback_db_path"`
// ProvisionTimeout is the maximum time allowed for the entire provisioning
// operation (image pull + container creation + start). If exceeded, the
// provisioning is canceled and a failure callback is sent.
ProvisionTimeout time.Duration `yaml:"provision_timeout"`
// HostBindIP is the IP address to bind container ports to.
// Defaults to "0.0.0.0" (all interfaces).
HostBindIP string `yaml:"host_bind_ip"`
// NetworkIsolation enables per-lease Docker network isolation.
// When true, each lease's containers are placed in the lease's own bridge
// network, isolated from every other lease, including leases of the same
// tenant. The network is created with Internal:false
// — required for port publishing (moby#36174) — so containers retain outbound
// internet access as a side effect. Defaults to true.
NetworkIsolation *bool `yaml:"network_isolation"`
// ContainerReadonlyRootfs sets the container's root filesystem to read-only.
// When true, /tmp and /run are mounted as tmpfs. Defaults to true.
ContainerReadonlyRootfs *bool `yaml:"container_readonly_rootfs"`
// RuntimeRefreshMaxConcurrency bounds how many leases one operator-started
// runtime refresh recreates at once (POST /runtime_refresh). Unset uses
// the default of 4; 0 disables the endpoint.
RuntimeRefreshMaxConcurrency *int `yaml:"runtime_refresh_max_concurrency"`
// ContainerPidsLimit limits the number of PIDs in each container.
// Defaults to 256.
ContainerPidsLimit *int64 `yaml:"container_pids_limit"`
// ContainerTmpfsSizeMB sets each memory-tmpfs ceiling and, separately, the
// conservative host-disk scratch allowance pinned for every DiskMB==0
// instance. Scratch is physical admission authority but is not retainable.
// Zero selects the 64 MB default.
ContainerTmpfsSizeMB int `yaml:"container_tmpfs_size_mb"`
// StartupVerifyDuration is how long to wait after starting containers before
// verifying they're still running. This catches containers that crash immediately
// on startup (e.g., bad config, read-only filesystem errors, missing dependencies).
// The success callback is only sent after verification passes.
// Defaults to 5 seconds. Setting to 0 uses the default (verification cannot be disabled).
StartupVerifyDuration time.Duration `yaml:"startup_verify_duration"`
// TenantQuota configures per-tenant resource limits.
// When set, prevents any single tenant from consuming the entire pool.
TenantQuota *TenantQuotaConfig `yaml:"tenant_quota"`
// VolumeDataPath is the host directory for managed durable and writable-path
// scratch volumes. It is required when any SKU profile has DiskMB > 0 and
// optional for an all-diskless deployment. A diskless instance gets a
// quota-enforced subdirectory only when its image needs writable-path
// scaffolding; provisioning remains best-effort when this path is omitted.
VolumeDataPath string `yaml:"volume_data_path"`
// VolumeMountPath is the operator-declared mountpoint containing
// VolumeDataPath. It is required whenever managed host volumes are enabled,
// including diskless writable-path scratch, and must be an actual mount at
// initialization and runtime. This independent deployment fact
// prevents an unmounted same-filesystem stub from being sealed as a fresh
// storage lineage after whole-data-root loss.
VolumeMountPath string `yaml:"volume_mount_path"`
// VolumeFilesystem specifies the filesystem type for volume quota enforcement.
// Supported values: "btrfs", "xfs", "zfs". If empty, auto-detected from VolumeDataPath.
VolumeFilesystem string `yaml:"volume_filesystem"`
// MinAvgFileBytes is the smallest average file size (bytes) a volume's workload
// may have before the per-volume XFS inode quota (ihard) — rather than the block
// quota (bhard) — binds. It is the mkfs "-i bytes-per-inode" idiom applied as an
// inode-ceiling ratio: ihard = DiskMB*MiB / MinAvgFileBytes (floored at 262144
// inodes). Smaller => MORE inodes allowed => looser cap. 0 uses the default
// (1024 => 1024 inodes/MiB).
// Values below 512 are rejected (they would uncap the fix). See ENG-548.
MinAvgFileBytes int64 `yaml:"min_avg_file_bytes"`
// CallbackMaxAge is the maximum age of a legacy callback or typed lifecycle
// observation. Exact operation and maintenance completions never expire
// because they may be the only evidence that settles Fred's durable placement
// or replacement intent. It must be positive. Defaults to 24h.
CallbackMaxAge time.Duration `yaml:"callback_max_age"`
// DiagnosticsDBPath is the path to the bbolt database for persisting failure diagnostics.
// Defaults to "diagnostics.db".
DiagnosticsDBPath string `yaml:"diagnostics_db_path"`
// DiagnosticsMaxAge is the maximum age of a persisted diagnostic entry.
// Entries older than this are removed by the diagnostics store's background cleanup.
// Defaults to 7 days.
DiagnosticsMaxAge time.Duration `yaml:"diagnostics_max_age"`
// ReleasesDBPath is the path to the bbolt database for persisting release history.
// Defaults to "releases.db".
ReleasesDBPath string `yaml:"releases_db_path"`
// ReleasesMaxAge is the maximum age of a persisted release entry.
// Entries older than this are removed by the release store's background cleanup.
// Defaults to 90 days.
ReleasesMaxAge time.Duration `yaml:"releases_max_age"`
// RetainOnClose controls whether a lease's managed VOLUMES are soft-deleted
// (renamed into the fred-retained- namespace and recorded in the retention
// store) instead of immediately destroyed when the lease is closed. The
// lease's containers are still stopped and removed either way; only the
// volumes are retained. When false (default), the volumes are destroyed
// immediately on close.
RetainOnClose bool `yaml:"retain_on_close"`
// RetentionDBPath is the path to the bbolt database for persisting
// soft-deleted leases awaiting restore or reaping.
// Defaults to "retention.db".
RetentionDBPath string `yaml:"retention_db_path"`
// RetentionMaxAge is the grace window after which a soft-deleted lease
// becomes eligible for reaping. 0 disables reaping entirely.
// Defaults to 90 days.
RetentionMaxAge time.Duration `yaml:"retention_max_age"`
// RetentionReapInterval is how often the background reaper sweeps for
// expired soft-deleted leases. Decoupled from RetentionMaxAge so the
// sweep cadence can be tuned independently of the grace window.
// Defaults to 1h.
RetentionReapInterval time.Duration `yaml:"retention_reap_interval"`
// MaxRetainedLeasesPerTenant caps how many soft-deleted leases a single
// tenant may have in the retention store at once. 0 means unlimited.
MaxRetainedLeasesPerTenant int `yaml:"max_retained_leases_per_tenant"`
// RetentionOrphanConfirmations is the number of consecutive retention sweeps a
// soft-deleted record must be observed with ALL its retained volumes missing
// before the record is pruned (ENG-370). It is a SWEEP COUNT, not a duration:
// the effective confirmation window is N × RetentionReapInterval (≈3h at the
// default 1h interval), so shortening RetentionReapInterval proportionally
// shrinks the window — re-tune N to keep a fixed grace. 0 is valid and
// disables orphan pruning entirely (kill-switch); negative values are rejected
// by Validate. Defaults to 3.
RetentionOrphanConfirmations int `yaml:"retention_orphan_confirmations"`
// MaxRetainedDiskMB caps the aggregate disk (MB) the provider will hold in
// the retained (soft-deleted) tier across ALL tenants. When retaining a
// closing lease would push the total over this cap, the lease is destroyed
// immediately instead of retained (refuse-to-retain) — never evicting
// another tenant's in-grace data. 0 means unlimited (default; retained
// volumes still count against total_disk_mb via the admission gate, but are
// not separately bounded). Must be <= total_disk_mb and, when set, >= the
// largest single stateful SKU's disk_mb (else a SKU-legal lease could never
// be retained). Independent of tenant_quota.max_disk_mb: it may be smaller,
// in which case a tenant's max-sized lease is SKU-legal yet refused
// retention. Value is a plain integer in MB (mebibytes, 2^20 bytes —
// consistent with total_disk_mb and the SKU disk_mb fields).
MaxRetainedDiskMB int64 `yaml:"max_retained_disk_mb"`
// RetentionPartitionSource selects where the OPTIONAL retention-partition
// key is read from at lease close. "" (default) disables partitioning —
// behavior is byte-identical to a build without the feature. Formats:
// "manifest.label:<key>" read from each service's labels map
// "manifest.env:<key>" read from each service's env map
// The extracted value only ever SUB-DIVIDES a tenant's retention budget
// (never raises any cap); non-allowlisted tenants' declarations collapse
// to the whole-tenant bucket. Invalid/unsatisfiable sources fail startup.
RetentionPartitionSource string `yaml:"retention_partition_source"`
// MaxRetainedDiskMBPerTenant caps one tenant's aggregate retained disk
// (MB). Same refuse-to-retain semantics as max_retained_disk_mb but
// per-tenant. 0 = unlimited (default; today's behavior). Overridden per
// tenant by a retention_tenant_budgets entry.
MaxRetainedDiskMBPerTenant int64 `yaml:"max_retained_disk_mb_per_tenant"`
// RetentionTenantBudgets is the aggregator allowlist: per-tenant retention
// budget overrides keyed by the on-chain tenant address (an opaque string —
// deliberately not bech32-validated; a typo'd key silently never matches,
// which the startup budget-sanity pass surfaces). Only tenants listed here
// with max_partitions > 0 ever persist a non-empty partition.
RetentionTenantBudgets map[string]RetentionTenantBudget `yaml:"retention_tenant_budgets"`
// Ingress configures optional reverse proxy integration.
// When enabled, containers with routable TCP ports get proxy labels
// pointing Traefik at the lease's own network for HTTPS auto-discovery.
// Requires network_isolation to be enabled.
Ingress IngressConfig `yaml:"ingress"`
}
Config holds the configuration for the Docker backend.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config with sensible defaults.
SKUProfiles is intentionally left empty: tier sizing is operator policy, not a code default. yaml.v3 merges map keys, so seeding defaults here would silently leak into any partial sku_profiles: block in YAML and trip the bidirectional sku_mapping/sku_profiles reachability check in Validate (see ENG-238). Operators must declare sku_profiles in their config; Validate enforces non-empty.
func (*Config) GetHostBindIP ¶
GetHostBindIP returns the configured bind IP, defaulting to "0.0.0.0".
func (*Config) GetMinAvgFileBytes ¶ added in v0.11.0
GetMinAvgFileBytes returns the configured minimum average file size in bytes, used to derive per-volume XFS inode limits. Defaults to 1024. See ENG-548.
func (*Config) GetPidsLimit ¶
GetPidsLimit returns the PID limit for containers. Defaults to 256.
func (*Config) GetRuntimeRefreshMaxConcurrency ¶ added in v0.14.0
GetRuntimeRefreshMaxConcurrency returns how many leases a runtime refresh recreates at once; 0 means the endpoint is disabled.
func (*Config) GetSKUProfile ¶
func (c *Config) GetSKUProfile(sku string) (SKUProfile, error)
GetSKUProfile returns the profile for a SKU, or an error if not found. It first checks if the SKU is a UUID that maps to a profile name via SKUMapping, then falls back to direct profile lookup.
func (*Config) GetTmpfsSizeMB ¶
GetTmpfsSizeMB returns the memory-tmpfs ceiling and diskless scratch allowance in MB. A configured zero selects the positive 64 MB default.
func (*Config) HasStatefulSKUs ¶
HasStatefulSKUs returns true if any SKU profile has DiskMB > 0, indicating that volume management is needed.
func (*Config) IsNetworkIsolation ¶
IsNetworkIsolation returns whether per-lease network isolation is enabled. Defaults to true (secure by default).
func (*Config) IsReadonlyRootfs ¶
IsReadonlyRootfs returns whether containers should have a read-only root filesystem. Defaults to true (secure by default).
func (*Config) RequestKeys ¶ added in v0.14.0
func (c *Config) RequestKeys() (hmacauth.VerifyKeys, error)
RequestKeys returns the keys accepted on providerd requests: callback_secret, which the backend also signs callbacks with, and the verify-only callback_secret_next during a rotation.
type ContainerEvent ¶
type ContainerEvent struct {
ContainerID string
Action string // one of the containerEvent* actions below
}
ContainerEvent represents a container lifecycle event from the Docker daemon. This keeps Docker SDK types out of the interface boundary.
type ContainerInfo ¶
type ContainerInfo struct {
ContainerID string
LeaseUUID string
Tenant string
ProviderUUID string
BackendName string
SKU string
ServiceName string // fred.service_name label: every current launch sets it ("app" for a single-service lease); only a legacy container may lack it
InstanceIndex int
FailCount int
CallbackURL string
LifecycleCallbackURL string
MaintenanceID shared.MaintenanceID
// RuntimeContract is the runtime contract recorded at creation; zero when
// the container predates the contract.
RuntimeContract runtimeContract
// Networks names the Docker networks the container is attached to, sorted,
// as the daemon reports them.
Networks []string
Image string
Status string
Health HealthStatus // Health check status (HealthStatusHealthy, HealthStatusUnhealthy, HealthStatusStarting, or HealthStatusNone)
ExitCode int // Process exit code (meaningful when Status is "exited"/"dead")
OOMKilled bool // True if container was killed by the OOM killer
CreatedAt time.Time
Ports map[string]PortBinding
FQDN string
CustomDomain string // Tenant-supplied custom FQDN (empty when not set)
// Name is the human-readable container name (without the leading "/"
// the Docker API prepends). Populated by both InspectContainer and
// ListManagedContainers. Used by the recover-time migration to filter
// out already-migrated `-prev` remnants.
Name string
// Mounts is the set of bind/volume mounts attached to the container.
// Populated by InspectContainer always; populated by
// ListManagedContainers from `types.Container.Mounts` (which the
// list-containers API includes inline, so no extra Inspect round-trip
// is needed at startup). Used by the recover-time migration to locate
// managed volumes that need renaming under the new naming convention.
Mounts []ContainerMount
// contains filtered or unexported fields
}
ContainerInfo holds information about a managed container.
type ContainerMount ¶
type ContainerMount struct {
Source string
Target string
Type string // "bind" | "volume" | "tmpfs"
ReadOnly bool
}
ContainerMount mirrors the subset of docker's Mount data fred needs for the recover-time migration: where the host bind comes from, where it's mounted inside the container, and the type discriminator (bind / volume / tmpfs). Type is a string per the docker API rather than the typed `mount.Type` because the list-containers and inspect-container APIs surface it as a free-form string and copying that semantics here keeps callers from caring about which API shape they're consuming.
type CreateContainerParams ¶
type CreateContainerParams struct {
Image imageexec.Image // admitted content required by the physical creation sink
LeaseUUID string
Tenant string
ProviderUUID string
SKU string
ServiceName string // Stack service name (empty for single-container leases)
Manifest *manifest.Manifest
Profile SKUProfile
InstanceIndex int // For multi-unit leases (0-based index)
// Retry tracking
FailCount int
// CallbackURL is persisted as a label so failure callbacks can be
// sent after a docker-backend restart (when in-memory state is lost).
CallbackURL string
// LifecycleCallbackURL is the typed observational endpoint used only
// after provisioning/restoration, for autonomous failure and teardown
// events. It is persisted separately so restart recovery cannot resurrect
// an expired operation capability as the lifecycle route.
LifecycleCallbackURL string
// MaintenanceID is empty for provision/restore and identifies the exact
// restart/update generation for replacement containers.
MaintenanceID shared.MaintenanceID
// Hardening parameters
HostBindIP string
ReadonlyRootfs bool
PidsLimit *int64
TmpfsSizeMB int
NetworkConfig *networktypes.NetworkingConfig
// VolumeBinds are bind mounts from host to container for managed volumes.
// Each entry maps a host path to a container path.
// Used for stateful containers (disk_mb > 0).
VolumeBinds map[string]string
// ImageVolumes are VOLUME paths from the image that need tmpfs overrides
// (for ephemeral containers only, when VolumeBinds is nil).
ImageVolumes []string
// WritablePathBinds are bind mounts from managed volume subdirectories
// to auto-detected writable directories in the container. Each entry
// maps a host path (with pre-extracted image content) to a container path.
WritablePathBinds map[string]string
// User overrides the container's runtime user (e.g., "999:999").
// When set, container.Config.User is set to this value so the container
// runs directly as the target user instead of relying on the entrypoint
// to switch users (which requires CAP_CHOWN that we drop).
User string
// BackendName identifies the backend instance that created this container,
// stored as a label to scope list/filter operations per backend.
BackendName string
// Ingress holds the reverse proxy configuration.
// When Enabled, proxy labels are injected into the container.
// NetworkName must be non-empty when Ingress is enabled (enforced by
// Config.Validate requiring network_isolation).
Ingress IngressConfig
NetworkName string // Network name for the traefik.docker.network label
Quantity int // Total quantity for this service (used in subdomain computation)
// CustomDomain is the optional tenant-supplied FQDN for this LeaseItem.
// When non-empty (and a routable HTTP port exists), CreateContainer
// emits a secondary Traefik router routing Host(<CustomDomain>) to the
// shared per-service loadbalancer. Validated defense-in-depth before
// emission; chain authoritatively validates upstream.
CustomDomain string
}
CreateContainerParams holds parameters for creating a container.
type DaemonSecurityInfo ¶
type DaemonSecurityInfo struct {
SystemID string
StorageDriver string
BackingFilesystem string
SecurityOptions []string
IPv4Forwarding bool
Warnings []string
}
DaemonSecurityInfo contains Docker daemon capabilities relevant to container hardening validation.
type DockerClient ¶
type DockerClient struct {
// contains filtered or unexported fields
}
DockerClient wraps the Docker client for container lifecycle operations.
func NewDockerClient ¶
NewDockerClient connects to Docker and requires the image execution API prerequisite before returning an executable client. The context is used only during construction. The backendName parameter scopes all list/filter/event operations so that multiple backend instances sharing the same Docker daemon do not interfere with each other. Every container the client creates, directly or through Compose, carries the process's tenant seccomp profile.
func (*DockerClient) AdmitImage ¶ added in v0.14.0
AdmitImage is image preparation: it validates image metadata and resolves an independently executable immutable image before any helper can be created.
func (*DockerClient) ContainerEvents ¶
func (d *DockerClient) ContainerEvents(ctx context.Context) (<-chan ContainerEvent, <-chan error)
ContainerEvents subscribes to Docker container lifecycle events on containers managed by fred (label fred.managed=true): "die", plus the "start" and "kill" events that attribute a death to its cause (ENG-799). The three share one subscription so the daemon's own ordering holds between them: dockerd logs "kill" after delivering an API signal and "die" from the exit handler, both under the container lock, so a signal to a run is always delivered before that run's death. Returns a channel of ContainerEvent and a channel of errors. Both channels are closed when the context is canceled or the Docker event stream closes.
func (*DockerClient) ContainerLogs ¶
func (d *DockerClient) ContainerLogs(ctx context.Context, containerID string, tail int) (string, error)
ContainerLogs returns the last `tail` lines of combined stdout/stderr for a container. If tail is <= 0 it defaults to 100.
func (*DockerClient) CreateContainer ¶
func (d *DockerClient) CreateContainer(ctx context.Context, params CreateContainerParams, timeout time.Duration) (string, error)
CreateContainer creates a new container with the specified configuration. For ephemeral port bindings, it retries up to portBindRetries times on port conflict errors since these can be transient.
func (*DockerClient) CreateLeaseNetwork ¶ added in v0.14.0
func (d *DockerClient) CreateLeaseNetwork(ctx context.Context, leaseUUID string) (leaseNetworkAuthority, error)
CreateLeaseNetwork creates the lease's network with its ownership labels and returns authority over it only after inspecting the ID the daemon returned. When another creator wins the name first, its network is used only if it proves to be this lease's on this backend.
func (*DockerClient) DaemonInfo ¶
func (d *DockerClient) DaemonInfo(ctx context.Context) (DaemonSecurityInfo, error)
DaemonInfo returns Docker daemon capabilities for hardening validation. It also records whether the daemon reports seccomp support, which the tenant profile's readiness gauge reflects.
func (*DockerClient) DetectVolumeOwner ¶
func (d *DockerClient) DetectVolumeOwner(ctx context.Context, imageName imageexec.Image, volumePaths []string, origin shared.ImageInspectionOrigin) (uid, gid int, err error)
DetectVolumeOwner inspects the ownership of VOLUME directories inside an image by creating a temporary container (never started) and reading the tar headers from CopyFromContainer. If all volume paths share the same non-root UID:GID, those values are returned. If the paths have mixed ownership, are owned by root, or a path doesn't exist, (0, 0, nil) is returned.
func (*DockerClient) DetectWritablePaths ¶
func (d *DockerClient) DetectWritablePaths(ctx context.Context, imageName imageexec.Image, uid int, candidateParents []string, origin shared.ImageInspectionOrigin) (paths []string, err error)
DetectWritablePaths scans candidate parent directories inside an image for depth-1 subdirectories owned by uid. When uid is 0 (root image), it matches directories owned by any non-root user — this handles images like neo4j that run as root but chown directories to a service user during build.
func (*DockerClient) ExtractImageContent ¶
func (d *DockerClient) ExtractImageContent(ctx context.Context, imageName imageexec.Image, paths []string, destDir string, maxBytes, maxEntries int64, origin shared.ImageInspectionOrigin) (failures map[string]error)
ExtractImageContent extracts pre-existing image content for the given paths into destDir on the host filesystem. For each path, it creates a temporary container from the image (never started), streams the tar archive of that path via CopyFromContainer, sanitizes the tar stream, and extracts it to the appropriate subdirectory under destDir.
Returns nil on full success, or a map of path → error for failures. Callers should log failures but not fail the provision (graceful degradation).
func (*DockerClient) InspectContainer ¶
func (d *DockerClient) InspectContainer(ctx context.Context, containerID string) (*ContainerInfo, error)
InspectContainer returns detailed information about a container.
func (*DockerClient) InspectLeaseNetwork ¶ added in v0.14.0
func (d *DockerClient) InspectLeaseNetwork(ctx context.Context, leaseUUID string) (authority leaseNetworkAuthority, present bool, err error)
InspectLeaseNetwork returns authority over the lease's existing network, or present=false when no network of that name exists. It changes nothing. An existing network is accepted only when it carries this lease's and backend's ownership labels; any other network of that name is refused and left untouched: never attached to, adopted, relabeled or removed.
func (*DockerClient) ListIdleManagedNetworks ¶ added in v0.14.0
func (d *DockerClient) ListIdleManagedNetworks(ctx context.Context) ([]networktypes.Inspect, error)
ListIdleManagedNetworks selects managed network cleanup candidates in one daemon query. A list result never grants removal authority: current provision ownership and an exact network inspect are rechecked immediately before removal.
func (*DockerClient) ListManagedContainers ¶
func (d *DockerClient) ListManagedContainers(ctx context.Context) ([]ContainerInfo, error)
ListManagedContainers returns all containers managed by Fred. When backendName is set, only containers belonging to this backend are returned.
func (*DockerClient) ListManagedContainersStrict ¶ added in v0.14.0
func (d *DockerClient) ListManagedContainersStrict(ctx context.Context) ([]ContainerInfo, error)
ListManagedContainersStrict is the identity-initialization variant. It refuses a managed container with malformed Fred labels rather than silently dropping it from the storage-lineage proof.
func (*DockerClient) ListVolumeWriters ¶ added in v0.14.0
func (d *DockerClient) ListVolumeWriters(ctx context.Context) ([]ContainerInfo, error)
ListVolumeWriters deliberately has no Fred label filter. An unmanaged or foreign container with access to a managed volume must remain visible as interference. Labels are observations here; exact stop authority requires a separate inspected generation match at the launch boundary.
func (*DockerClient) Ping ¶
func (d *DockerClient) Ping(ctx context.Context) error
Ping checks connectivity to the Docker daemon.
func (*DockerClient) RemoveContainer ¶
func (d *DockerClient) RemoveContainer(ctx context.Context, containerID string) error
RemoveContainer removes a container. It is idempotent — if the container is already gone or the daemon is already removing it, returns nil only after the removal has actually completed, so callers can safely proceed with operations that assume the container is physically gone (volume destroy, name re-use).
func (*DockerClient) RemoveLeaseNetworkIfEmpty ¶ added in v0.14.0
func (d *DockerClient) RemoveLeaseNetworkIfEmpty(ctx context.Context, authority leaseNetworkAuthority) (networkRemoval, error)
RemoveLeaseNetworkIfEmpty removes the exact network the authority names, if it still carries the lease's and this backend's ownership labels and no container is connected. It never resolves the network by name.
func (*DockerClient) RemoveTenantNetworkIfEmpty ¶
func (d *DockerClient) RemoveTenantNetworkIfEmpty(ctx context.Context, tenant string) (networkRemoval, error)
RemoveTenantNetworkIfEmpty removes the tenant's legacy network if it still carries this tenant's and backend's ownership labels and no container is connected. The verified ID, not the name, is removed.
func (*DockerClient) RenameContainer ¶
func (d *DockerClient) RenameContainer(ctx context.Context, containerID string, newName string) error
RenameContainer changes the name of a container. The container can be running or stopped. This is used during updates/restarts to free the canonical name for the replacement container while keeping the old one available for rollback.
func (*DockerClient) RequireImage ¶ added in v0.14.0
func (d *DockerClient) RequireImage(ctx context.Context, imageName string) error
RequireImage checks locally available executable content without changing the daemon image store. Registry ingestion requires the backend's capacity owner, its durable allocation ledger and a Started subject for pin publication.
func (*DockerClient) ResolveImageUser ¶
func (d *DockerClient) ResolveImageUser(ctx context.Context, imageName imageexec.Image, userOverride string, origin shared.ImageInspectionOrigin) (uid, gid int, err error)
ResolveImageUser resolves a container user specification to numeric UID/GID. If userOverride is non-empty, it is used instead of the image's Config.User. This is needed for images like postgres that start as root and expect to chown data directories — since we drop CAP_CHOWN, the manifest must specify the target user explicitly. If both userOverride and Config.User are empty, returns (0, 0, nil) (root). Numeric UID/GID values are parsed directly. Non-numeric usernames are resolved by reading /etc/passwd (and optionally /etc/group) from a temporary container created from the image.
func (*DockerClient) StartContainer ¶
func (d *DockerClient) StartContainer(ctx context.Context, containerID string, timeout time.Duration) error
StartContainer starts a container.
func (*DockerClient) StopContainer ¶
func (d *DockerClient) StopContainer(ctx context.Context, containerID string, timeout time.Duration) error
StopContainer gracefully stops a running container with a timeout. After the timeout, the container is forcefully killed.
func (*DockerClient) TenantSeccompCensus ¶ added in v0.14.0
func (d *DockerClient) TenantSeccompCensus(ctx context.Context) (tenantSeccompCensus, error)
TenantSeccompCensus lists every fred-managed container of this backend, whatever its labels say otherwise, inspects each through the SDK, and counts those running, restarting or paused without the current profile. It judges the unmodified HostConfig with dockerd's own rule: the last seccomp option is the one applied, and a privileged container has none. A container removed between list and inspect is skipped; any other failure fails the pass. It never changes a container.
type HealthStatus ¶
type HealthStatus string
HealthStatus represents the health check status of a Docker container.
const ( HealthStatusHealthy HealthStatus = "healthy" HealthStatusUnhealthy HealthStatus = "unhealthy" HealthStatusStarting HealthStatus = "starting" HealthStatusNone HealthStatus = "" // No health check configured )
type IngressConfig ¶
type IngressConfig struct {
Enabled bool `yaml:"enabled"`
WildcardDomain string `yaml:"wildcard_domain"`
Entrypoint string `yaml:"entrypoint"`
// CustomDomainCertResolver is the Traefik certresolver name used for
// per-tenant custom domains (HTTP-01 by default). Defaults to "http01"
// when empty.
CustomDomainCertResolver string `yaml:"custom_domain_cert_resolver"`
// CustomDomainMiddlewares is the list of Traefik middleware references
// applied to the secondary custom-domain router. Defaults to
// ["security-headers@file"] when empty.
CustomDomainMiddlewares []string `yaml:"custom_domain_middlewares"`
// CustomDomainDNSResolvers are the DNS servers (host:port) fred queries to
// check whether a tenant custom domain resolves yet before emitting its
// HTTP-01 router (ENG-266). Public resolvers are used so the answer matches
// what the ACME CA sees. Defaults to Cloudflare 1.1.1.1:53 /
// Google 8.8.8.8:53 / Quad9 9.9.9.9:53.
CustomDomainDNSResolvers []string `yaml:"custom_domain_dns_resolvers"`
// CustomDomainDNSQuorum is how many of the resolvers must independently see
// the domain resolve before the gate opens (ENG-266). 0 (default) ==
// a majority of CustomDomainDNSResolvers. Clamped to [1, len(resolvers)],
// or 1 when no resolvers are configured.
CustomDomainDNSQuorum int `yaml:"custom_domain_dns_quorum"`
// CustomDomainDNSCheckDisabled turns OFF the readiness gate (ENG-266),
// reverting to emitting the custom-domain router immediately. Default false.
CustomDomainDNSCheckDisabled bool `yaml:"custom_domain_dns_check_disabled"`
}
IngressConfig holds configuration for reverse proxy integration. When Enabled, containers with routable TCP ports get proxy labels pointing Traefik at the lease's own network for auto-discovery. Requires network_isolation to be enabled (validated at config load time). Currently generates Traefik-specific labels; the config layer is proxy-agnostic so a future backend swap only changes label generation.
The wildcard certificate covering *.WildcardDomain must be provisioned at the Traefik level (e.g. via a DNS-01 ACME resolver with domains in static config, or a default cert in tls.stores). Fred does not drive per-domain ACME challenges — routers are emitted with tls=true but name no certresolver, so Traefik serves whichever cert matches SNI.
func (*IngressConfig) Validate ¶
func (ic *IngressConfig) Validate() error
Validate checks that all required IngressConfig fields are set when enabled.
type PortBinding ¶
PortBinding represents a port mapping.
type RetentionTenantBudget ¶ added in v0.11.0
type RetentionTenantBudget struct {
MaxRetainedLeases int `yaml:"max_retained_leases"`
MaxRetainedDiskMB int64 `yaml:"max_retained_disk_mb"`
MaxPartitions int `yaml:"max_partitions"` // 0 = elevation-only: labels still collapse
PerPartitionMaxLeases int `yaml:"per_partition_max_leases"` // 0 = no L2 count sub-cap
PerPartitionMaxDiskMB int64 `yaml:"per_partition_max_disk_mb"` // 0 = no L2 disk sub-cap
}
RetentionTenantBudget overrides the default per-tenant retention caps for one allowlisted (aggregator) tenant and optionally enables partition sub-caps. Aggregates are REQUIRED positive: "unlimited" is unrepresentable inside a budget (load-bearing for the bounded-scan argument).
type RuntimeRefreshLease ¶ added in v0.14.0
type RuntimeRefreshLease struct {
LeaseUUID string `json:"lease_uuid"`
MaintenanceID string `json:"maintenance_id,omitempty"`
Outcome RuntimeRefreshOutcome `json:"outcome"`
Restored *RuntimeRefreshRestored `json:"restored,omitempty"`
}
RuntimeRefreshLease attests one lease's outcome. MaintenanceID is the runtime_refresh maintenance; a refreshed lease's containers carry it in fred.maintenance_id. It is empty when no refresh was admitted. Rows are built only by runtimeRefreshSettledRow, from a lease's durable receipt, and by the unsettled-row constructors, so refreshed and Restored rows always name their maintenance and Restored appears only on a failed row.
type RuntimeRefreshOutcome ¶ added in v0.14.0
type RuntimeRefreshOutcome string
RuntimeRefreshOutcome is one lease's result in a run.
const ( // RuntimeRefreshRefreshed: the lease's refresh target release is durably // active. Its new containers carry the attested maintenance ID in // fred.maintenance_id. RuntimeRefreshRefreshed RuntimeRefreshOutcome = "refreshed" // RuntimeRefreshFailed: the refresh did not become the active release. // When its settlement proved the pre-refresh release the running, ready // cohort, the row carries Restored: a compensation recreated it (copies with // new container IDs) or the target failed before displacing it. RuntimeRefreshFailed RuntimeRefreshOutcome = "failed" // RuntimeRefreshSkipped: the lease was not refreshed. It was busy with // other lifecycle work, or stopped being eligible, when last attempted. RuntimeRefreshSkipped RuntimeRefreshOutcome = "skipped" )
type RuntimeRefreshRestored ¶ added in v0.14.0
type RuntimeRefreshRestored struct {
ReleaseVersion int `json:"release_version"`
ReleaseDigest string `json:"release_digest"`
}
RuntimeRefreshRestored names the release a failed refresh settled back onto as the lease's running, ready cohort.
type RuntimeRefreshStatus ¶ added in v0.14.0
type RuntimeRefreshStatus struct {
Contract uint32 `json:"contract"`
Active bool `json:"active"`
Stale int `json:"stale"`
StaleLeases []string `json:"stale_leases"`
InFlight int `json:"in_flight"`
Refreshed int `json:"refreshed"`
Failed int `json:"failed"`
Restored int `json:"restored"`
Skipped int `json:"skipped"`
Leases []RuntimeRefreshLease `json:"leases"`
}
RuntimeRefreshStatus is the GET /runtime_refresh response.
Stale, StaleLeases and SharedNetworkLeases are read from Docker for each response. StaleLeases lists, sorted, the Ready leases with a container under an older contract, and Stale is its length; a lease whose refresh failed and was restored stays listed. SharedNetworkLeases lists, sorted, the leases with a container on a Docker network that a container of another lease is also on, in any state.
Leases holds one row per lease, sorted and unique by lease_uuid. A lease's refreshed or failed row comes from its latest retained runtime_refresh receipt, so it survives a backend restart and later runs; a skipped row, or a failure with no receipt, comes from the current or most recent run in this process. Active and InFlight describe that run. The per-outcome row counts always equal Refreshed, Failed and Skipped, and Restored counts the failed rows that carry Restored.
func (RuntimeRefreshStatus) MarshalJSON ¶ added in v0.14.0
func (s RuntimeRefreshStatus) MarshalJSON() ([]byte, error)
MarshalJSON writes nil lists as empty arrays: the cutover reads both as lists, so the wire never carries null for either.
type SKUProfile ¶
type SKUProfile = shared.SKUProfile
Type aliases for readability within the docker package.
type StoppedDrainReport ¶ added in v0.14.0
type StoppedDrainReport struct {
// Pending counts the journal's pending work, excluding the delete-held
// heads: every row InspectCallbackStoreReadOnly counted as pending, less
// DeleteHeld.
Pending int
// DeleteHeld counts the close and operation heads whose own lease's
// remaining volume slots all have a delete stage on disk, at least one
// of them. A restart registers each such stage as a held deletion and
// finishes it in the background. It is a reading of the volume root
// only: see ClassifyStoppedDrain for what it does not check.
DeleteHeld int
}
StoppedDrainReport is the coordinated update's hold-aware reading of a stopped docker-backend's callback journal (ENG-1117, ENG-1109).
A held volume deletion keeps the close or operation that waits on it pending, and only the hold executor can finish it; a deletion an operator must unblock can stay held for days. Counting such a head as pending work would refuse every coordinated update, after the fleet was already stopped, for as long as one deletion is held. DeleteHeld separates those heads so the deploy coordinator can decide not to block on them.
func ClassifyStoppedDrain ¶ added in v0.14.0
func ClassifyStoppedDrain(inspection shared.CallbackStoreInspection, volumeDataPath string) (StoppedDrainReport, error)
ClassifyStoppedDrain splits a stopped backend's pending work into ordinary pending work and delete-held heads. It is the native classifier of the coordinated-update drain proof, which manifest-deploy builds from this source and runs against a STOPPED backend: it reads the journal inspection it is given and makes one read-only listing of volumeDataPath's top level. An empty volumeDataPath (no managed volumes) holds nothing.
A close or operation head is delete-held when awaitsOnlyHeldDeletes holds over its own lease's volume slots, read from on-disk delete stages. That is only the volume-slot half of the running backend's close-churn skip (closeAwaitsHeldDeletes), and it excuses more than that skip does:
- operation heads: the running backend applies the predicate to closes only, and retries a pending operation as usual;
- every close, without the container half: whether a container of the lease remains is not checked, and a stage's phase is memory-only and not known here;
- its own lease's slots only: work an operation still owes in another lease's namespace (a restore returning its source volumes to retention, which can wait on a source container) is not seen.
DeleteHeld therefore does not prove that held deletions are the only work left. A restarted hold-aware backend re-checks all of it before it skips a close, and retries everything else.
type StorageIdentityAdoptionAudit ¶ added in v0.14.0
type StorageIdentityAdoptionAudit struct {
Verdict StorageIdentityAdoptionVerdict `json:"verdict"`
Findings []AdoptionAuditFinding `json:"findings"`
ActiveReleases []AdoptionAuditRelease `json:"active_releases"`
PendingCallbacks int `json:"pending_callbacks"`
}
StorageIdentityAdoptionAudit is the machine-readable result of the read-only adoption audit: every refused shape at once, in the preflight's order, so the first finding is exactly the error the preflight would report.
func AuditStorageIdentityAdoptionForConfig ¶ added in v0.14.0
func AuditStorageIdentityAdoptionForConfig( ctx context.Context, cfg Config, logger *slog.Logger, ) (StorageIdentityAdoptionAudit, error)
AuditStorageIdentityAdoptionForConfig is the read-only counterpart of the adoption preflight that reports every refused shape instead of the first. Preconditions the preflight cannot continue past (unreadable journals or substrate, a sealed or resuming lineage, a substrate that changes during the audit) are errors, not findings.
type StorageIdentityAdoptionVerdict ¶ added in v0.14.0
type StorageIdentityAdoptionVerdict string
StorageIdentityAdoptionVerdict is the stable machine-readable result of the stopped-v0.13 read-only preflight. Its zero value is never returned on success, so callers cannot mistake an incomplete check for admission.
const StorageIdentityAdoptionBlocked StorageIdentityAdoptionVerdict = "v0_13_storage_identity_adoption_blocked"
StorageIdentityAdoptionBlocked is the audit verdict when any shape refuses adoption.
const ( // StorageIdentityAdoptionReady proves that the same descriptor-bound // evidence used by adopt currently permits a seal. Initialization must still // repeat the proof after the operator's backup because the substrate can // change between commands. StorageIdentityAdoptionReady StorageIdentityAdoptionVerdict = "ready_for_v0_13_storage_identity_adoption" )
func PreflightStorageIdentityAdoptionForConfig ¶ added in v0.14.0
func PreflightStorageIdentityAdoptionForConfig( ctx context.Context, cfg Config, logger *slog.Logger, ) (StorageIdentityAdoptionVerdict, error)
PreflightStorageIdentityAdoptionForConfig performs the exact stopped-v0.13 adoption evidence proof without publishing or recovering markers and without binding or normalizing a journal. It is safe to run before the cutover backup; a successful initializer repeats the proof at its own publication boundary.
type StorageIdentityInitializationMode ¶ added in v0.14.0
type StorageIdentityInitializationMode string
StorageIdentityInitializationMode makes the operator's first-adoption claim explicit. The zero value is invalid.
const ( StorageIdentityInitializeNew StorageIdentityInitializationMode = "new" StorageIdentityInitializeAdopt StorageIdentityInitializationMode = "adopt" )
type TenantQuotaConfig ¶
type TenantQuotaConfig = shared.TenantQuotaConfig
Source Files
¶
- adoption_audit.go
- backend.go
- background_maintenance.go
- capability.go
- capability_linux.go
- close_failure_diagnostics.go
- close_intent.go
- close_substrate.go
- compensation_codec.go
- compensation_startup.go
- compose.go
- compose_project.go
- compose_service_names.go
- config.go
- container_event_loop.go
- container_event_reader.go
- custom_domain_dns.go
- daemon_launch.go
- deprovision.go
- diagnostic_callback_publisher.go
- doc.go
- docker_sdk.go
- failure_diagnostics.go
- health_metrics.go
- image_admission.go
- image_cache_ownership.go
- image_capacity.go
- image_flight.go
- image_inspection.go
- image_metadata_transport.go
- image_pin_backfill.go
- image_security.go
- image_tenant.go
- info.go
- ingress.go
- ingress_plan.go
- launch_resources.go
- lease_actor_factory.go
- lease_actor_routing.go
- lease_network.go
- leasesm_adapters.go
- leasesm_metrics.go
- lifecycle.go
- live_death_ledger.go
- maintenance_compensation.go
- maintenance_intent.go
- maintenance_projection.go
- metrics.go
- operation_input.go
- operation_intent.go
- physical_execution.go
- physical_instance_inspection.go
- projid_audit.go
- provision.go
- provision_predecessor.go
- quota_backfill.go
- reconcile_custom_domain.go
- recover.go
- recovered_operation_failure.go
- recovered_provision.go
- recovery_budget.go
- recovery_deadline.go
- recovery_repair.go
- request_auth_metrics.go
- resource_profiles.go
- restart_update.go
- restore.go
- retention.go
- retention_accounting.go
- retention_writable_path.go
- runtime_contract.go
- runtime_refresh.go
- settled_launch.go
- startup_failure.go
- startup_health_ledger.go
- startup_observation.go
- stopped_drain.go
- storage_mutation_guard.go
- substrate_read_views.go
- teardown.go
- tenant_seccomp.go
- terminal_substrate_recovery.go
- tree_removal.go
- v013_release_authority.go
- v013_upgrade.go
- volume.go
- volume_access.go
- volume_accounting_observation.go
- volume_btrfs.go
- volume_compensation.go
- volume_delete_hold.go
- volume_destroy.go
- volume_launch.go
- volume_launch_debt.go
- volume_lock.go
- volume_name.go
- volume_namespace.go
- volume_namespace_root.go
- volume_writer_inventory.go
- volume_writer_retirement.go
- volume_xfs.go
- volume_xfs_delete_hold.go
- volume_zfs.go
- writable_path_detection.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package imageexec admits immutable container images and binds their use to typed Docker and Compose execution sinks.
|
Package imageexec admits immutable container images and binds their use to typed Docker and Compose execution sinks. |
|
Package imagefetch admits registry content before giving Docker any bytes.
|
Package imagefetch admits registry content before giving Docker any bytes. |
|
Package tenantseccomp owns the seccomp profile fred applies to every container it creates for a tenant, and every Docker security option that names a seccomp profile.
|
Package tenantseccomp owns the seccomp profile fred applies to every container it creates for a tenant, and every Docker security option that names a seccomp profile. |