capability

package
v1.0.0-rc5 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package capability names the optional interfaces a backend may implement.

The reconciler and the sweep hold a charts.Backend, which is the smallest contract that can deploy a stack and little else. Everything beyond it that they ask a backend for — read a ServiceSpec, list what a stack declared, scope an image pull to one application's credential, count the swarm's nodes — is an upgrade they type-assert for, so a backend that cannot answer loses that one feature instead of failing. *backend.Backend implements every one of them, and asserts so at compile time; a Phase 3 remote backend reached through the same swarms seam implements whichever it can.

They live here rather than beside the callers that assert for them because they are the contract such a backend is written against, and an unexported contract is not one. Go interfaces are structural, so a companion in another module *can* satisfy an interface it cannot name — but it cannot be compile-checked against it, and since every one of these is an optional upgrade that falls back silently when the assertion fails, the companion would learn about a changed signature by watching a feature quietly stop working rather than from a build error.

A package of its own rather than twelve more exported names in reconcile and prune. This repository's exported surface *is* the companion contract, so where a name lives is most of what says whether it is one: these are, and the reconciler's own Fetcher, Builder and Engine — which it states for itself and nobody outside implements — are not. It also leaves both of those packages' public surfaces exactly as they were, and gives a backend one import rather than two.

Nothing here has an implementation, and no fallback lives here either. What a caller does when an assertion fails is different for every one of these — unchanged backend, no live drift, no sweep, no purge — so it stays with the caller that has to choose.

Adding one is the same commitment as adding a seam method: the day the companion ships, every signature in this file is frozen, and a capability that needs to grow grows by taking a struct rather than by widening a parameter list.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AllowedReferences

type AllowedReferences interface {
	WithAllowedReferences(application.Allow) charts.Backend
}

AllowedReferences is the optional interface a backend implements to take one application's allowlist of what its charts may reach outside their own releases.

A backend that does not implement it enforces its own. The refusal belongs where the specs are written rather than in the reconciler, so that nothing can reach a swarm past a layer that did not look.

type DeclaredLister

type DeclaredLister interface {
	DeclaredResources(ctx context.Context, manifest, stack string) (*compose.Stack, error)
}

DeclaredLister is the optional interface a backend implements to answer what a manifest declares without needing any of it to exist.

LiveDrift.DesiredServices cannot answer that question about a *stored* revision. Converting a service resolves every config and secret it mounts to the id Swarm addresses it by, so it asks today's swarm about yesterday's references — and a revision that mounted a config a previous sweep has since deleted no longer converts at all. The sweep then loses that revision's claims and leaves resources behind (#87). Nothing about proving ownership wants an id; the scoped name is the whole of it.

Separate from LiveDrift rather than added to it, for the reason ResourceLister gives: a backend that cannot answer this should lose the sweep's history walk, not its live drift too. And separate from DesiredServices rather than replacing it, because live drift compares whole ServiceSpecs and must never start diffing against a placeholder id.

type ForbidSecrets

type ForbidSecrets interface {
	WithForbiddenSecrets(map[string]struct{}) charts.Backend
}

ForbidSecrets is the optional interface a backend implements to refuse a stack mounting the controller's own secrets.

type LiveDrift

type LiveDrift interface {
	DesiredServices(ctx context.Context, manifest, stack string) (*compose.Stack, error)
	LiveServices(ctx context.Context, stack string) (map[string]swarm.Service, error)
}

LiveDrift is the optional interface a backend implements to expose the two halves of a live drift comparison.

It exists because charts.Backend carries no way to read a ServiceSpec — its StackServices returns a display projection with a running count and no spec — and no Docker client for the reconciler to convert a manifest with. A backend that cannot answer does not implement it, and its applications report no live drift rather than failing.

Only the read half needs a capability. Correcting drift is DeployStack, which is on charts.Backend already.

type NetworkNamer

type NetworkNamer interface {
	LiveNetworkNames(ctx context.Context) (map[string]string, error)
}

NetworkNamer is the optional interface a backend implements to name the networks a service is attached to.

A spec names them by id, because the daemon rewrites each target to one as it writes the service, so without this the attachments cannot be compared at all. Separate from LiveDrift for the reason ResourceLister is: a backend that can read services but not list networks should lose that one field and keep every other comparison.

Not stack-scoped, unlike ResourceLister's three. A service may be attached to an external network or a predefined one, neither of which carries the stack's namespace label, and an attachment that could not be named is exactly the one worth reporting.

type OutOfBand

type OutOfBand interface {
	WithOutOfBandNotifier(func(service string)) charts.Backend
}

OutOfBand is the optional interface a backend implements to report a mutation that lost its compare-and-swap.

Swarm gives the controller exactly one signal that something else is writing to a service — ?version= is mandatory, so a mutation that loses the race is refused — and only the reconciler knows which application a write belongs to, which is why the backend reports rather than notifies.

type RegistryAuth

type RegistryAuth interface {
	WithRegistryAuth(regauth.Resolver) charts.Backend
}

RegistryAuth is the optional interface a backend implements to authenticate image pulls with an application's credential. A backend reached through the swarms seam that authenticates its own way need not, and is left unchanged.

type ResourceLister

type ResourceLister interface {
	LiveNetworks(ctx context.Context, stack string) (map[string]string, error)
	LiveConfigs(ctx context.Context, stack string) (map[string]string, error)
	LiveSecrets(ctx context.Context, stack string) (map[string]string, error)
}

ResourceLister is the optional interface a backend implements to read the other three kinds a manifest declares, by scoped name.

Separate from LiveDrift because live drift compares services and nothing else: a backend that can answer one and not the other should lose only the half it cannot answer. A backend implementing neither prunes nothing, which is the degradation live drift already has.

Name to id is all the sweep needs — it matches on the scoped name, the only key a manifest and a live resource share, and deletes by id.

type ResourceRemover

type ResourceRemover interface {
	RemoveService(ctx context.Context, id string) error
	RemoveNetwork(ctx context.Context, id string) error
	RemoveConfig(ctx context.Context, id string) error
	RemoveSecret(ctx context.Context, id string) error
}

ResourceRemover is the optional interface a backend implements to delete a single resource of each kind.

Separate from the readers rather than folded into them, so that a backend which can read the swarm but not write to it still reports what a sweep would remove instead of losing the report along with the removal.

type StackServicesReader

type StackServicesReader interface {
	ReadStackServices(ctx context.Context, name string) ([]charts.ServiceState, error)
}

StackServicesReader is the optional interface a backend implements to report a failed read rather than an empty stack.

charts.Backend.StackServices has no error return, and for its CE caller that is right: it polls, so a daemon that could not be asked is "not converged yet" and the next poll asks again. A caller that instead publishes what it read turns the same nil into "deployed, but no services are present on the swarm", the loudest thing a health rollup can say — so one slow daemon flipped every release of an application from healthy to missing (#107).

type StacksReader

type StacksReader interface {
	ReadStacks(ctx context.Context, releases []string) (map[string][]charts.ServiceState, error)
}

StacksReader is the optional interface a backend implements to answer for several releases from one look at the swarm.

The read behind StackServices is a whole-swarm snapshot — NodeList, ServiceList, TaskList and Info — which is then filtered by stack name. Asking it once per release fetches the entire swarm once per release and throws away all but one stack's worth each time.

The contract is all-or-nothing: a nil error means every requested release is in the map, and a non-nil one means the swarm could not be read and none of them are. That is what one snapshot actually does, and it is what keeps a daemon that could not be asked from being published as a swarm with no services on it.

type SwarmSizer

type SwarmSizer interface {
	SwarmNodes(ctx context.Context) (int, error)
}

SwarmSizer is the optional interface a backend implements to count the swarm's nodes.

A backend that does not cannot say whether a node-local volume listing was the whole swarm's, which for a deletion has to mean the same as knowing it was not.

Jump to

Keyboard shortcuts

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