Documentation
¶
Overview ¶
Package docker is a Docker-backed provider.Provider. It maps each ctrlplane Instance to one Docker container, named "cp-<instanceID>" so lookups by ID are O(1) inspect calls.
ManagedDockerExecutor provides a separate single-mutation custody path. You supply an authenticated authority, effect verifier, protected journal and pinned transport. It records delegation durably before sending. A lost response stays unresolved, and replay never sends again. Creation retains the actual Docker ID; removal uses that exact ID and treats NotFound as uncertainty.
FileManagedDockerJournal requires a private local POSIX directory outside restored controller database state. SDKManagedDockerTransport supports configured Unix sockets or TCP endpoints with optional verified TLS. Images must already be present by digest; the adapter never pulls or implicitly starts a container.
These components do not implement the complete managed provider or recovery profile. Your host still owns authenticated enrollment, fresh writer admission, restore quarantine and native lifecycle integration.
Index ¶
- Constants
- Variables
- type Config
- type FileManagedDockerJournal
- type ManagedDockerAuthority
- type ManagedDockerCommand
- type ManagedDockerCustody
- type ManagedDockerExecutor
- func (e *ManagedDockerExecutor) Execute(ctx context.Context, request ManagedDockerRequest) (ManagedDockerRecord, error)
- func (e *ManagedDockerExecutor) Prepare(ctx context.Context, request ManagedDockerRequest) (ManagedDockerRecord, error)
- func (e *ManagedDockerExecutor) Read(ctx context.Context, request ManagedDockerRequest) (ManagedDockerRecord, error)
- func (e *ManagedDockerExecutor) RecoverProviderEffectBinding(ctx context.Context, original managed.ProviderEffectOriginal) (managed.ProviderEffectBindingFacts, error)
- func (e *ManagedDockerExecutor) WitnessManagedProviderNoDispatch(ctx context.Context, key managed.ProviderInvocationKey) (managed.ProviderInvocationFailure, error)
- type ManagedDockerJournal
- type ManagedDockerRecord
- type ManagedDockerRequest
- type ManagedDockerResponse
- type ManagedDockerRetryAuthority
- type ManagedDockerRetryEvidence
- type ManagedDockerStartup
- type ManagedDockerStartupResolver
- type ManagedDockerTransport
- type Option
- type Provider
- func (p *Provider) Capabilities() []provider.Capability
- func (p *Provider) Deploy(ctx context.Context, req provider.DeployRequest) (*provider.DeployResult, error)
- func (p *Provider) Deprovision(ctx context.Context, instanceID id.ID) error
- func (p *Provider) Exec(_ context.Context, _ id.ID, _ provider.ExecRequest) (*provider.ExecResult, error)
- func (p *Provider) HealthCheck(ctx context.Context) (*provider.HealthStatus, error)
- func (p *Provider) Info() provider.ProviderInfo
- func (p *Provider) Logs(ctx context.Context, instanceID id.ID, opts provider.LogOptions) (io.ReadCloser, error)
- func (p *Provider) Provision(ctx context.Context, req provider.ProvisionRequest) (*provider.ProvisionResult, error)
- func (p *Provider) Resources(ctx context.Context, instanceID id.ID) (*provider.ResourceUsage, error)
- func (p *Provider) Restart(ctx context.Context, instanceID id.ID) error
- func (p *Provider) Rollback(_ context.Context, _ id.ID, _ id.ID) error
- func (p *Provider) Scale(_ context.Context, _ id.ID, _ provider.ResourceSpec) error
- func (p *Provider) Start(ctx context.Context, instanceID id.ID) error
- func (p *Provider) Status(ctx context.Context, instanceID id.ID) (*provider.InstanceStatus, error)
- func (p *Provider) Stop(ctx context.Context, instanceID id.ID) error
- type SDKManagedDockerTransport
Constants ¶
const ManagedDockerJournalLimit = 65536
ManagedDockerJournalLimit bounds a complete row, including base64 encoded evidence.
const ManagedDockerProfile = "ctrlplane.docker.custody.v1"
ManagedDockerProfile identifies exact single-mutation Docker transport custody.
Variables ¶
var ErrLogsNotImplemented = errors.New("docker: logs not implemented")
ErrLogsNotImplemented is returned when log streaming is requested but the docker exec/logs path hasn't been wired yet.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// Host is the Docker daemon address (e.g., unix:///var/run/docker.sock).
Host string `env:"CP_DOCKER_HOST" json:"host"`
// Network is the Docker network to attach containers to.
Network string `default:"bridge" env:"CP_DOCKER_NETWORK" json:"network"`
// Registry is the default image registry prefix.
Registry string `env:"CP_DOCKER_REGISTRY" json:"registry,omitempty"`
}
Config holds configuration for the Docker provider.
type FileManagedDockerJournal ¶ added in v1.8.0
type FileManagedDockerJournal struct {
// contains filtered or unexported fields
}
FileManagedDockerJournal is a local POSIX custody adapter. Its directory must be pre-created, owned by the executor UID, mode 0700, on a filesystem supporting flock, atomic same-directory rename and file/directory fsync. It is not an NFS adapter. The trusted launcher must protect its storage and quarantine snapshot restore. This adapter cannot detect privileged rollback or provide cross-host writer admission. There is no deletion API. Closed and delegated rows survive executor restart.
func OpenFileManagedDockerJournal ¶ added in v1.8.0
func OpenFileManagedDockerJournal(directory string) (*FileManagedDockerJournal, error)
OpenFileManagedDockerJournal opens an existing protected directory. It neither creates deployment storage nor enrolls an executor or enables a managed profile.
func (*FileManagedDockerJournal) CompareAndSwap ¶ added in v1.8.0
func (j *FileManagedDockerJournal) CompareAndSwap(ctx context.Context, key string, expected, next []byte) (bool, error)
CompareAndSwap syncs bytes, atomically renames, and syncs the directory before success. Cancellation or I/O error after rename may leave the transition retained. The caller must recover by reading and must not infer that a failed claim permits a send.
type ManagedDockerAuthority ¶ added in v1.8.0
type ManagedDockerAuthority interface {
VerifyRequest(ctx context.Context, request ManagedDockerRequest) error
Admit(ctx context.Context, request ManagedDockerRequest) error
SealResponse(ctx context.Context, request ManagedDockerRequest, response ManagedDockerResponse) (ManagedDockerCustody, error)
VerifyCustody(ctx context.Context, request ManagedDockerRequest, response ManagedDockerResponse, custody ManagedDockerCustody) error
NoDispatch(ctx context.Context, request ManagedDockerRequest, closedDigest string) (managed.ProviderInvocationFailure, error)
VerifyNoDispatch(ctx context.Context, request ManagedDockerRequest, closedDigest string, witness managed.ProviderInvocationFailure) error
}
ManagedDockerAuthority is trusted configuration, not a request-selected resolver. VerifyRequest authenticates retained permission, receipt, exact transport semantics, backend identity and any original resource/process custody. Admit additionally checks current authority and the newly issued one-shot grant/marker before delegation. SealResponse recovers stable enrollment evidence without launching or sending Docker commands. VerifyCustody authenticates that evidence against this exact response. NoDispatch signs the permanently closed journal fact; its witness must bind the digest. Implementations must refuse absent enrollment, stale/replaced processes and restore quarantine. The library supplies no permissive default or external launcher setup.
type ManagedDockerCommand ¶ added in v1.8.0
type ManagedDockerCommand struct {
Kind managed.ProviderStepKind `json:"kind"`
Name string `json:"name"`
ResourceID string `json:"resource_id"`
NetworkID string `json:"network_id"`
Service *managed.ProvisionService `json:"service"`
}
ManagedDockerCommand contains non-secret inputs to one Docker mutation. Resource and network IDs are actual create response IDs. Names are creation hints only.
type ManagedDockerCustody ¶ added in v1.8.0
type ManagedDockerCustody struct {
ResourceID string `json:"resource_id"`
Incarnation string `json:"incarnation"`
OriginalProcess *managed.OriginalProcessFacts `json:"original_process"`
Attestation []byte `json:"attestation"`
}
ManagedDockerCustody adds independently authenticated creation/incarnation facts. OriginalProcess is required for start; it is never synthesized from Docker inspection.
type ManagedDockerExecutor ¶ added in v1.8.0
type ManagedDockerExecutor struct {
// contains filtered or unexported fields
}
ManagedDockerExecutor owns a journal and configured authority/transport pair. It is an independently usable single-step component, not the complete ManagedProvider.
func NewManagedDockerExecutor ¶ added in v1.8.0
func NewManagedDockerExecutor(journal ManagedDockerJournal, transport ManagedDockerTransport, authority ManagedDockerAuthority, verifier managed.ProviderEffectBindingVerifier, backend string) (*ManagedDockerExecutor, error)
NewManagedDockerExecutor refuses missing or typed-nil trusted dependencies.
func (*ManagedDockerExecutor) Execute ¶ added in v1.8.0
func (e *ManagedDockerExecutor) Execute(ctx context.Context, request ManagedDockerRequest) (ManagedDockerRecord, error)
Execute claims durable delegation before calling transport once. Replay of delegated refuses, even when the earlier call may have failed before reaching Docker.
func (*ManagedDockerExecutor) Prepare ¶ added in v1.8.0
func (e *ManagedDockerExecutor) Prepare(ctx context.Context, request ManagedDockerRequest) (ManagedDockerRecord, error)
Prepare retains authenticated original inputs without granting a send. Exact replay succeeds; any altered command, permission, receipt or enrollment association refuses.
func (*ManagedDockerExecutor) Read ¶ added in v1.8.0
func (e *ManagedDockerExecutor) Read(ctx context.Context, request ManagedDockerRequest) (ManagedDockerRecord, error)
Read observes exact retained facts without producing a delegation grant.
func (*ManagedDockerExecutor) RecoverProviderEffectBinding ¶ added in v1.8.0
func (e *ManagedDockerExecutor) RecoverProviderEffectBinding(ctx context.Context, original managed.ProviderEffectOriginal) (managed.ProviderEffectBindingFacts, error)
RecoverProviderEffectBinding only returns evidence retained before the transport.
func (*ManagedDockerExecutor) WitnessManagedProviderNoDispatch ¶ added in v1.8.0
func (e *ManagedDockerExecutor) WitnessManagedProviderNoDispatch(ctx context.Context, key managed.ProviderInvocationKey) (managed.ProviderInvocationFailure, error)
WitnessManagedProviderNoDispatch closes prepared custody by the same CAS used by Execute. Once delegated, neither absence nor errors can produce this witness.
type ManagedDockerJournal ¶ added in v1.8.0
type ManagedDockerJournal interface {
Load(ctx context.Context, key string) ([]byte, error)
CompareAndSwap(ctx context.Context, key string, expected, next []byte) (bool, error)
}
ManagedDockerJournal is a protected durable byte CAS. Empty expected means absent. CompareAndSwap must be atomic across processes and durable before success. An error can mean committed but unacknowledged. Callers must never send after such an error. Rows and permanent closed/delegated states must not be deleted or restored backwards.
type ManagedDockerRecord ¶ added in v1.8.0
type ManagedDockerRecord struct {
SchemaVersion managed.Decimal `json:"schema_version"`
Request ManagedDockerRequest `json:"request"`
State string `json:"state"`
Response *ManagedDockerResponse `json:"response"`
Custody *ManagedDockerCustody `json:"custody"`
Closure *managed.ProviderInvocationFailure `json:"closure"`
}
ManagedDockerRecord is a bounded observation. Only complete has authenticated custody. delegated means possible effect; observed retains a response awaiting authentication. Neither state permits resend. Closed is a permanent affirmative no-dispatch fact.
type ManagedDockerRequest ¶ added in v1.8.0
type ManagedDockerRequest struct {
BackendAuthority string `json:"backend_authority"`
Permission managed.WireProviderStepPermission `json:"permission"`
Receipt managed.WireReceipt `json:"receipt"`
Command ManagedDockerCommand `json:"command"`
Binding *managed.ProviderEffectBindingFacts `json:"binding"`
Invocation *managed.ProviderInvocationPermission `json:"invocation"`
Retry *ManagedDockerRetryEvidence `json:"retry,omitempty"`
}
ManagedDockerRequest retains exact permission and independent receipt custody. Nonzero removal invocations require accepted Retry evidence and explicit authority.
type ManagedDockerResponse ¶ added in v1.8.0
type ManagedDockerResponse struct {
ResourceID string `json:"resource_id"`
ObservedAt time.Time `json:"observed_at"`
}
ManagedDockerResponse records an actual successful API response, not authenticated process custody. In particular a returned creation ID does not establish enrollment.
type ManagedDockerRetryAuthority ¶ added in v1.8.0
type ManagedDockerRetryAuthority interface {
VerifyRetry(ctx context.Context, request ManagedDockerRequest) error
AdmitRetry(ctx context.Context, request ManagedDockerRequest) error
}
ManagedDockerRetryAuthority explicitly opts the configured authority into retries. VerifyRetry must reload the exact accepted records and independent receipts from the configured core store, including the retained retry request/event, predecessor closure certificate, root selection and native proof/commit associations. It may inspect historical records but must never manufacture fresh authority from them. AdmitRetry must additionally authenticate current native binding and consume the volatile grant from a newly accepted marker before transport. Reopening a saved marker, journal or receipt cannot recreate this grant. An error may consume it. These methods supplement full VerifyRequest; no permissive default is supplied.
type ManagedDockerRetryEvidence ¶ added in v1.8.0
type ManagedDockerRetryEvidence struct {
Root managed.RecoveryRecord[managed.ProviderStepRetryRoot] `json:"root"`
Permission managed.RecoveryRecord[managed.ProviderInvocationPermission] `json:"permission"`
Authorization managed.RecoveryRecord[managed.ProviderStepRetryAuthorization] `json:"authorization"`
Marker managed.RecoveryRecord[managed.ProviderInvocationMarker] `json:"marker"`
}
ManagedDockerRetryEvidence retains the accepted source and exact retry invocation. Marker is required before preparing transport custody. None of these records grants another send; the configured authority must independently authenticate all of them.
type ManagedDockerStartup ¶ added in v1.8.0
type ManagedDockerStartup struct {
Entrypoint []string `json:"entrypoint"`
Arguments []string `json:"arguments"`
Environment []string `json:"environment"`
}
ManagedDockerStartup is private fixed startup material. It is never journaled or emitted as evidence. The configured resolver authenticates the complete descriptor.
type ManagedDockerStartupResolver ¶ added in v1.8.0
type ManagedDockerStartupResolver interface {
ResolveManagedDockerStartup(ctx context.Context, service managed.ProvisionService) (ManagedDockerStartup, error)
}
ManagedDockerStartupResolver is read-only trusted host configuration. It must refuse stale enrollment, descriptor substitution or missing private startup inputs.
type ManagedDockerTransport ¶ added in v1.8.0
type ManagedDockerTransport interface {
Execute(ctx context.Context, command ManagedDockerCommand) (string, error)
}
ManagedDockerTransport sends exactly one mutation, with no retry, adoption, name removal, compensating cleanup, pull, or NotFound-to-success conversion.
type Option ¶ added in v1.3.0
Option configures a Docker provider.
func WithConfig ¶ added in v1.3.0
WithConfig applies all non-zero fields from a Config struct. This is useful when loading configuration from files or environment variables.
func WithHost ¶ added in v1.3.0
WithHost sets the Docker daemon address (e.g., "unix:///var/run/docker.sock").
func WithNetwork ¶ added in v1.3.0
WithNetwork sets the Docker network to attach containers to.
func WithRegistry ¶ added in v1.3.0
WithRegistry sets the default image registry prefix.
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider is a Docker-based infrastructure provider. Each Instance becomes one container; the container name encodes the instance ID for stateless lookups (no per-instance ProviderRef storage needed beyond the canonical "docker:<container_name>" string).
func New ¶
New creates a new Docker provider. Uses standard docker env (DOCKER_HOST, DOCKER_TLS_VERIFY, etc.) by default; pass WithHost to pin a specific socket. Network defaults to "bridge" but should be overridden to a named network when the provider is colocated with a router (e.g. Traefik) that needs to reach containers by DNS name.
func (*Provider) Capabilities ¶
func (p *Provider) Capabilities() []provider.Capability
Capabilities returns the set of features this provider supports.
func (*Provider) Deploy ¶
func (p *Provider) Deploy(ctx context.Context, req provider.DeployRequest) (*provider.DeployResult, error)
Deploy applies a new image / env to an existing container by recreating it. Docker doesn't support in-place image swap, so the implementation is: stop+remove the current container, then create a fresh one with the new spec. Same name, same network, new image.
For the initial provision case (where Provision already ran with the right image) Deploy is effectively a no-op — the container stays as-is. The caller (ctrlplane deploy.Service) treats success here as "release applied" and bumps the deployment state. Deploy applies per-service image / env updates, and any command, args, ports or resources the request sets, by recreating only the targeted containers within the live project network. Services not listed in req.Services keep running unchanged.
Per-service rollout for a multi-container project is "stop & replace each targeted container in turn" — Docker doesn't support in-place image swap. The Compose-project network stays up across the recreation so sibling services don't drop their network namespace.
func (*Provider) Deprovision ¶
Deprovision tears down every container in the project + the project network. Not-found is treated as success so deprovision is convergent — the goal is "project gone", not strict accounting.
func (*Provider) Exec ¶
func (p *Provider) Exec(_ context.Context, _ id.ID, _ provider.ExecRequest) (*provider.ExecResult, error)
Exec is not yet implemented.
func (*Provider) HealthCheck ¶ added in v1.5.1
HealthCheck pings the docker daemon and reports reachability. The HealthChecker contract is "is this provider's control plane reachable" — so we use cli.Ping (which round-trips to the daemon) rather than docker info / version which load more state.
func (*Provider) Info ¶
func (p *Provider) Info() provider.ProviderInfo
Info returns metadata about this provider. Local Docker has a fixed region of "local" with a default localhost location so studio catalog endpoints surface a usable region in dev environments where no datacenters have been registered yet.
func (*Provider) Logs ¶
func (p *Provider) Logs(ctx context.Context, instanceID id.ID, opts provider.LogOptions) (io.ReadCloser, error)
Logs returns a stream of structured JSON log events (one {"ts":..., "stream":"stdout"|"stderr", "line":"..."} per line) from the container's stdout + stderr. When opts.Follow is true the stream stays open and emits new lines as the container writes them; the caller closes the returned ReadCloser to stop.
Honours opts.Tail (last N lines, "0" = all), opts.Since (only lines newer than this UTC time), and opts.Follow. Multiplexed docker frames are demuxed by demuxedDockerStream.
func (*Provider) Provision ¶
func (p *Provider) Provision(ctx context.Context, req provider.ProvisionRequest) (*provider.ProvisionResult, error)
Provision pulls the image, creates a container with the requested ports/env/labels, starts it, and returns the resulting endpoints.
Endpoints are emitted in two flavours per declared port:
- in-network: http://cp-<instanceID>:<containerPort> — reachable from other containers on the same docker network. Preferred for service-to-service routing (Traefik joins this network and uses these URLs upstream).
- public: http://localhost:<hostPort> — the random host port docker assigned. Useful when hitting a workspace from outside the docker network without a host-side proxy.
Re-provisioning the same instance ID is idempotent: any existing container with the same name is force-removed first so the create succeeds with the fresh config.
func (*Provider) Resources ¶
func (p *Provider) Resources(ctx context.Context, instanceID id.ID) (*provider.ResourceUsage, error)
Resources returns a one-shot point-in-time sample of the container's CPU / memory / network usage via the docker stats API. The non-streaming variant gives us a single JSON document per call, which is what the metrics poller wants — it calls us every N seconds and stores the result in its own ring buffer.
Returns a zero-valued ResourceUsage (no error) when the container doesn't exist or the stats response is empty — the metrics poller treats "missing sample" as a gap, not a failure, so transient container-restart windows don't show up as poller errors.
func (*Provider) Restart ¶
Restart cycles every Main + Sidecar container in the project. Inits stay completed — restart is for live workload bouncing, not init re-runs.
func (*Provider) Rollback ¶
Rollback is a no-op today — docker has no built-in release history, so rollback would have to recreate using the previous image which the deploy service already knows about and could route through Deploy.
func (*Provider) Scale ¶
Scale is a no-op for the docker provider — single-container instances don't have a horizontal scale axis. The kubernetes provider is the right home for replica scaling.
func (*Provider) Start ¶
Start starts every Main + Sidecar container in the project. Init services are not restarted — they ran-once during provision.
func (*Provider) Status ¶
Status aggregates inspect across every container in the project. Worst-of state wins (any Failed → Failed; otherwise any Restarting → Starting; otherwise any not-Running → Stopped). Ready is true only when every Main + Sidecar reports Running. Init containers don't gate readiness once they've completed (they appear as Stopped/exited 0 — the non-Init aggregation ignores them).
type SDKManagedDockerTransport ¶ added in v1.8.0
type SDKManagedDockerTransport struct {
// contains filtered or unexported fields
}
SDKManagedDockerTransport implements the fixed exact mutation profile. The client must be pinned by trusted configuration to the executor's backend authority. A raw client/socket is an authority-bearing dependency and must never be request supplied.
func NewSDKManagedDockerTransport ¶ added in v1.8.0
func NewSDKManagedDockerTransport(host, apiVersion string, transport *http.Transport, startup ManagedDockerStartupResolver) (*SDKManagedDockerTransport, error)
NewSDKManagedDockerTransport fixes the host/API version without environment lookup. The supplied transport carries trusted TLS credentials. Only canonical Unix sockets and explicit TCP host:port endpoints are supported. Custom dialers and proxies refuse; the adapter owns exact endpoint dialing. TLS uses verified HTTPS without fallback. Connection reuse, HTTP/2 and redirects are disabled to prevent transparent replay.
func (*SDKManagedDockerTransport) Close ¶ added in v1.8.0
func (t *SDKManagedDockerTransport) Close() error
Close releases this adapter's client connections without changing retained custody.
func (*SDKManagedDockerTransport) Execute ¶ added in v1.8.0
func (t *SDKManagedDockerTransport) Execute(ctx context.Context, command ManagedDockerCommand) (string, error)
Execute issues one mutation and returns the actual create response ID or exact removal/start target ID. Docker 404 remains uncertain, never terminal success.