Documentation
¶
Overview ¶
Package backend applies compose-derived Swarm specs to a swarm through the moby client. It is the half of charts.Backend that needs a daemon; turning a manifest into those specs is package compose.
Index ¶
- Constants
- func MountedSecretNames(dir string) (map[string]struct{}, error)
- type Backend
- func (b *Backend) ApplyServices(ctx context.Context, stack *cdcompose.Stack, resolve string) error
- func (b *Backend) CreateConfig(ctx context.Context, name string, data []byte, labels map[string]string) error
- func (b *Backend) CreateOverlayNetwork(ctx context.Context, name, driver string, attachable bool) error
- func (b *Backend) DeleteConfig(ctx context.Context, name string) error
- func (b *Backend) DeployStack(name, manifest, resolve string) error
- func (b *Backend) InspectConfig(ctx context.Context, name string) ([]byte, error)
- func (b *Backend) ListConfigs(ctx context.Context) ([]charts.ConfigMeta, error)
- func (b *Backend) NetworkScopes(ctx context.Context) (map[string]string, error)
- func (b *Backend) RefreshSnapshot() error
- func (b *Backend) RemoveOverlayNetwork(ctx context.Context, name string) error
- func (b *Backend) RemoveStack(name string) error
- func (b *Backend) RemoveVolume(ctx context.Context, name string) error
- func (b *Backend) SecretNames(ctx context.Context) (map[string]struct{}, error)
- func (b *Backend) StackServices(name string) []charts.ServiceState
- func (b *Backend) StackVolumes(ctx context.Context, name string) ([]string, error)
- func (b *Backend) WithForbiddenSecrets(names map[string]struct{}) charts.Backend
- func (b *Backend) WithRegistryAuth(auth regauth.Resolver) charts.Backend
- type Options
Constants ¶
const ( // ResolveAlways asks the registry to resolve the tag to a digest on every // deploy. Docker's default. ResolveAlways = "always" // ResolveChanged resolves only when the manifest names a different image // than the last deploy did, which is what suits automation: an unchanged // tag does not become a redeploy just because the registry moved. ResolveChanged = "changed" // ResolveNever leaves the tag as written. ResolveNever = "never" )
Image resolution modes, matching charts.InstallOptions.ResolveImage and the daemon's own query parameter.
Variables ¶
This section is empty.
Functions ¶
func MountedSecretNames ¶
MountedSecretNames returns the names of the secrets Swarm has mounted into this controller, by listing dir (each secret is a file at /run/secrets/<name>). That set is exactly what a reconciled stack must not be allowed to mount: the admin token, the git token and every registryAuth are the controller's own credentials, and a stack mounting one by an `external` reference would read it.
A dir that does not exist yields an empty set and no error: a controller run outside a swarm has no mounted secrets to protect, and must still start.
Types ¶
type Backend ¶
type Backend struct {
// contains filtered or unexported fields
}
Backend applies specs to one swarm.
func (*Backend) ApplyServices ¶
ApplyServices creates the stack's services that do not exist and updates those that do, in the order the stack lists them.
It deletes nothing. Phase 1 is explicitly no prune, and charts.Apply itself never deletes either — a service the manifest no longer declares is left alone, not reaped.
It also cannot detect an out-of-band change. Swarm has no server-side apply, so `docker service update --replicas 10` produces no conflict signal at all: the next reconcile simply computes the same desired spec and writes it back, silently. The only conflict this can see is a write that races one of ours.
func (*Backend) CreateConfig ¶
func (b *Backend) CreateConfig(ctx context.Context, name string, data []byte, labels map[string]string) error
CreateConfig stores one release revision.
The extra swarmcli.created label matches what the CE backend writes, so a release recorded by this controller and one recorded from the command line look the same to the TUI's config view.
func (*Backend) CreateOverlayNetwork ¶
func (*Backend) DeleteConfig ¶
func (*Backend) DeployStack ¶
DeployStack converges the swarm to a rendered manifest.
Order matters and is the same order `docker stack deploy` uses: the things a service can reference have to exist before the service that references them. Volumes are absent from that list on purpose — Swarm creates a named volume on the node that first needs it, so there is nothing to pre-create.
Nothing is deleted. Phase 1 is explicitly no prune.
func (*Backend) InspectConfig ¶
func (*Backend) ListConfigs ¶
ListConfigs returns every config's name and labels.
One ConfigList call, not a list followed by an inspect per config. The CE backend inspects each one because the TUI wants CreatedAt, which ConfigList omits — but the engine only reads Name and Labels here, and this runs on every reconcile against a store that grows by one config per release revision.
func (*Backend) NetworkScopes ¶
NetworkScopes maps every network's name to its scope, for the engine's external-network pre-flight.
func (*Backend) RefreshSnapshot ¶
RefreshSnapshot is a no-op: this backend holds no cache to invalidate.
The method exists because the ambient CE backend reads through a process-wide snapshot with a 3s TTL and has to be told when it has gone stale. Here every read fetches, which is what makes one process able to serve several swarms without them evicting each other's state.
func (*Backend) RemoveOverlayNetwork ¶
RemoveOverlayNetwork removes a network by name, rolling back one this engine auto-created for an install whose deploy then failed. A network that is already gone is not an error: the caller is undoing, and undoing something that did not happen has succeeded.
func (*Backend) RemoveStack ¶
RemoveStack deletes the services, networks, configs and secrets carrying the stack's namespace label — what `docker stack rm` removes, and nothing more.
Removal is idempotent, and it is decided by looking rather than by reading the error. A resource that has already gone between the list and the delete has reached the state this was asking for; a failure that leaves nothing behind is not a failure. That is not a rare race — Swarm garbage-collects an overlay network once the last task attached to it goes, which happens while the services removed a few lines above are still shutting down, so the network this listed is routinely gone before it is asked to remove it.
Classifying the error is not enough on its own. A swarm-scoped network removal is proxied through swarmkit and its "already gone" reply does not reliably arrive as a not-found the client recognises, so every failed removal is followed by a re-check of what is actually left. The state is the answer; the error is only a hint about where to look.
This is what makes the call safely repeatable, which prune's retry depends on: a pass that failed part-way is followed by another that re-lists and re-deletes, and treating the already-deleted half as an error would make that retry fail forever.
Volumes survive, as they do there: a stack's data outliving the stack is the whole point of a named volume, and charts has RemoveVolume for the caller that means it.
The engine's own release records are untouched. They are Docker configs, but they carry com.swarmcli.* labels rather than a stack namespace, so the filter below cannot see them — which is what lets a release be uninstalled and its history still be readable.
func (*Backend) RemoveVolume ¶
func (*Backend) SecretNames ¶
SecretNames is the set of existing secret names, for the engine's pre-flight. An external secret cannot be auto-created — its content is exactly what the manifest does not carry — so this only ever answers "is it there".
func (*Backend) StackServices ¶
func (b *Backend) StackServices(name string) []charts.ServiceState
StackServices reads one stack's live service states.
Every rule in here belongs to the chart engine and is reached through its own exported mapping (Eldara-Tech/swarmcli#508): the running count by actual rather than desired state (#480), the target over active nodes (#481), a completed one-shot job counting toward its target instead of reading 0/N (#443, #494). A second copy would diverge silently, and both directions of that are wrong — reporting a release converged while the engine would still be waiting, or degraded on a stack that is fine.
A snapshot that cannot be read returns nil, matching the CE backend: the caller polls, so an unavailable daemon is "not converged yet" rather than a failure to report.
func (*Backend) StackVolumes ¶
StackVolumes names the volumes carrying this stack's namespace label.
func (*Backend) WithForbiddenSecrets ¶
WithForbiddenSecrets returns a copy of the backend that refuses to deploy a stack mounting any of the named secrets — the controller's own credentials. Controller-wide, not per application, but applied through the same optional-interface upgrade as WithRegistryAuth so the reconciler need not depend on this concrete type.
func (*Backend) WithRegistryAuth ¶
WithRegistryAuth returns a copy of the backend that authenticates its image pulls with auth. The copy shares the client — one swarm's connection pool is not duplicated per application — and differs only by the resolver, so the per-swarm backend stays shared while the credential stays per application.
It returns charts.Backend so the reconciler can reach it through the swarms seam (which hands back that interface) with an optional-interface upgrade, rather than depending on this concrete type.