Documentation
¶
Overview ¶
Package drift decides whether an application matches git.
There are two modes and they answer different questions. driftDetection: manifest compares what the repository renders to against what was last applied, which catches a changed chart version, changed values and a changed template, but says nothing about an operator running `docker service update` behind the controller's back. driftDetection: live is what sees that one, by comparing each settled release's running ServiceSpec against the manifest; it is in live.go, which also says what it does and does not compare.
The manifest decision comes out of the chart engine's plan. The engine already canonicalises values through a YAML round trip and compares the rendered manifest string, so asking it to plan is exactly as authoritative as its own apply — and it is the same work the sync would do, rather than an approximation of it.
Index ¶
- func Application(releases []application.ReleaseStatus) *application.Drift
- func Diffs(plan *charts.Plan) []application.ReleaseDiff
- func FromPlan(plan *charts.Plan) (application.Sync, []application.ReleaseStatus)
- func Live(desired *compose.Stack, live map[string]swarm.Service, nets NetworkNames) *application.ReleaseDrift
- type NetworkNames
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Application ¶
func Application(releases []application.ReleaseStatus) *application.Drift
Application rolls each release's live drift up to one state for the whole application, or nil when no release was compared — which is every application running the default manifest mode.
Detected outranks Unknown deliberately, matching how health ranks Degraded over Missing: a difference that was found is something to act on now, whereas one that could not be read is a gap in the reporting. Both are said; only one leads.
func Diffs ¶
func Diffs(plan *charts.Plan) []application.ReleaseDiff
Diffs renders the manifest change each release would undergo.
Unchanged releases are omitted: their diff is empty by construction, and a response listing them would make a synced application look like it had twenty things to say.
The rendering is the chart engine's own (utils/textdiff), so what the API serves is character-for-character what `swarmcli charts apply --diff` prints. Two renderings of the same change that disagree in whitespace would be worse than either alone.
func FromPlan ¶
func FromPlan(plan *charts.Plan) (application.Sync, []application.ReleaseStatus)
FromPlan maps a plan to an application's sync status and its per-release detail.
The returned Sync carries no Revision and no LastSync: a plan does not know which commit produced it, or what happened the last time one was applied. The reconciler fills both. ReleaseStatus.Revision is likewise left zero — the chart revision number lives in the release records, not in the plan.
Plan.Orphaned and Plan.Unmanaged are deliberately not surfaced. They now classify against this controller's own owner id rather than the command line's (Eldara-Tech/swarmcli#499, adopted in #27), so they are at least about the right releases — but they answer a per-swarm question, not a per-application one: with several applications on one swarm, each would report the others' releases as unmanaged.
func Live ¶
func Live(desired *compose.Stack, live map[string]swarm.Service, nets NetworkNames) *application.ReleaseDrift
Live compares what a release's manifest declares against what is running.
Why this is an allowlist ¶
A ServiceSpec read back from the daemon is not the one that was written. Swarmkit defaults fields the manifest never mentioned, resolves images to digests, and returns empty maps where the caller sent nil. Comparing whole specs therefore reports permanent drift on a service nobody has touched, which is worse than no drift detection at all — it is the failure that makes an operator turn the feature off, and after that it detects nothing.
So this compares a named set of fields and nothing else. The boundary is what `docker service update` can change, which is not an arbitrary line: it is the out-of-band mutation surface this exists to observe. A field is added to the list only with a normalisation that a real swarm has been observed to satisfy — see integration-tests/live_drift_test.go, which deploys a stack using every one of them and asserts that an untouched deploy reports nothing.
Everything outside the list is documented as not compared rather than silently missed; docs/configuration.md carries that list.
Why not YAML ¶
Diffing a reconstructed compose file against the manifest would read better and be wrong: compose → ServiceSpec is lossy and one-way, so the reconstruction manufactures differences that do not exist. Established with a reproduction in swarmcli-cd#1 and restated in CLAUDE.md.
Naming what the daemon stores as an id ¶
One compared field cannot be read without help. A service's attached networks are stored by id where the manifest named a network, so nets carries the swarm's networks by id and the comparison names them before comparing. Nil is allowed and means the backend could not answer: attachments are then not compared, which loses one field rather than the whole report.
Types ¶
type NetworkNames ¶
NetworkNames maps a network's id to its name.
Nil means the backend could not list the swarm's networks, in which case attached networks are not compared. That is the degradation the sweep's capability.ResourceLister already takes: lose the one field that needed the read, not the whole comparison.