Documentation
¶
Overview ¶
Package resolve turns the source env's image occurrences into the pinned references a promotion writes, from what the env is running first, then its manifests, then the registry — never a guess (AGENTS.md §4.2, principle 3).
Resolve is pure orchestration over two interfaces, k8s.Cluster and registry.Registry, and is tested entirely against their in-memory fakes. Its input is plain data (the namespace, the occurrences, the source order, the caller's overrides) and its output is one Resolution per image repo: the reference to write, which source supplied the digest, the alternatives the other sources offered, and the warnings that explain every disagreement. It holds no credentials and opens no connection of its own (§4.3).
The rules, in the order they apply to one image repo:
- A caller override (`--digest`) wins over every source: it decides the reference. It does not silence the sources — a running disagreement or a running-vs-manifest split is still a warning, and the other sources' digests are still listed as alternatives.
- Sources are consulted in the caller's order (default pods, manifest, registry). The first that yields a digest is the Source of the resolution; a later source that yields a different digest is an alternative and, for pods against manifest, a running-vs-manifest warning. The registry is only asked when nothing before it in the order answered — it is the fallback of §4.2, not a cross-check.
- pods: the containers running that repo in the source namespace (k8s.Cluster decides which count). One digest across them all is the answer. Several digests are a running-disagrees warning naming every pod, container and digest, and a stated choice: the digest the manifest pins if it is among them, else the most frequent, ties to the lexically smallest. Which repo a container runs is the repo its imageID names, compared through image.Canonical, so a docker.io alias matches and a mirror does not.
- manifest: the pin the manifests carry, chosen by gitops.ChooseRef — the same rule BuildPlan uses, so a repo whose occurrences disagree gets the same reference here as there.
- registry: HEAD of the manifest's tag. For a multi-arch image that is the index digest, which is what a pull by tag pins and what imageID reports (see pkg/registry).
- The written reference is <repo>:<tag>@<digest> with the manifest's tag. A repo whose manifests carry no tag at all is left unresolved: pods cannot supply one (imageID has none) and hoist never fabricates one, so BuildPlan's existing tagless refusal stands.
- A repo no source can answer is unresolved: its Resolution has no Ref and an unresolved warning saying what each source found. It is left out of the digests map so BuildPlan's own rules — refuse to write a bare tag, warn when there is nothing to write — decide, unchanged.
Why the order is the caller's and pods lead by default: the brief for this milestone states both "the running digest wins when the pods agree" and "a pinned manifest that disagrees with the pods defaults to the manifest". Those conflict in exactly one case (pods agree on X, manifest pins Y). §4.2 says the digest comes from what the source env is running, so pods lead by default and the manifest pin is the alternative; the order flag (`--digest-sources manifest,pods,registry`) gives the other reading verbatim. Either way the disagreement is a warning, never silent.
Failure shape: an error from the cluster fails Resolve — the operator asked for the running digests and silently planning from the registry instead would promote a tag's current digest rather than what staging runs. An error from the registry is scoped to the one repo it was asked about (unresolved, with the registry's message), since it is the last source and every other repo may still resolve.
Index ¶
- Constants
- Variables
- func Digests(res map[string]Resolution) map[string]image.Ref
- func Reasons(res map[string]Resolution) map[string]string
- func Repos(res map[string]Resolution) []string
- func Resolve(ctx context.Context, in Input, cluster k8s.Cluster, reg registry.Registry) (map[string]Resolution, error)
- func Warnings(res map[string]Resolution) []gitops.Warning
- type Input
- type Resolution
- type Source
Constants ¶
const ( // WarnRunningDisagrees: the running containers of one repo carry different digests. WarnRunningDisagrees = "running-disagrees" // WarnRunningVsManifest: the pods run one digest and the manifest pins another — a // rollout may be incomplete, or a bump has not synced yet. WarnRunningVsManifest = "running-vs-manifest" // WarnUnresolved: no source could supply a writable reference for the repo. WarnUnresolved = "unresolved" )
Warning codes emitted by Resolve.
Variables ¶
var DefaultOrder = []Source{SourcePods, SourceManifest, SourceRegistry}
DefaultOrder is the chain when the caller states none: what runs, what the manifest pins, what the registry says the tag is.
Functions ¶
func Digests ¶
func Digests(res map[string]Resolution) map[string]image.Ref
Digests is the BuildPlan digests argument: every resolved repo's reference. Unresolved repos are absent, so BuildPlan's own rules decide them.
func Reasons ¶
func Reasons(res map[string]Resolution) map[string]string
Reasons names, per resolved repo, where its digest came from, in the words a plan's source-disagrees warning should use for the override Digests hands it: "resolved from pods" for a running-pod digest, and the resolution's own Detail for a caller override (issue #25).
func Repos ¶
func Repos(res map[string]Resolution) []string
Repos lists the resolved repos in order.
func Resolve ¶
func Resolve(ctx context.Context, in Input, cluster k8s.Cluster, reg registry.Registry) (map[string]Resolution, error)
Resolve resolves every repo among in.Occurrences. See the package doc for the rules. cluster may be nil when pods is not in the order; reg may be nil when registry is not.
Types ¶
type Input ¶
type Input struct {
// Namespace is the source env: the namespace whose pods are read.
Namespace string
// Occurrences are the source env's occurrences to resolve, promotable ones only — the
// caller filters; every repo among them gets a Resolution.
Occurrences []gitops.Occurrence
// Order is the sources to consult, first wins; empty resolves nothing.
Order []Source
// Overrides are caller-supplied references by repo (`--digest`); each wins outright.
Overrides map[string]image.Ref
}
Input is everything Resolve needs beyond its adaptors: plain data, so the call keeps the activity shape of AGENTS.md §4.3. It replaces the positional arguments the M2 brief sketched because the namespace to list pods in is not carried by an Occurrence and had to be added.
type Resolution ¶
type Resolution struct {
Repo string
// Ref is the reference to write: <repo>:<tag>@<digest>. Zero when unresolved.
Ref image.Ref
// Source is where Ref's digest came from; "" when unresolved.
Source Source
// Detail says how the source decided, for the plan's resolution section.
Detail string
// Alternatives are the other digests the sources offered, as references with the same
// repo and tag, in source order then lexical order; empty when everything agreed.
Alternatives []image.Ref
Warnings []gitops.Warning
}
Resolution is the outcome for one image repo.
func (Resolution) Resolved ¶
func (r Resolution) Resolved() bool
Resolved reports whether Ref can be written: it carries both a tag and a digest.
type Source ¶
type Source string
Source names where a digest came from.
const ( SourcePods Source = "pods" SourceManifest Source = "manifest" SourceRegistry Source = "registry" SourceOverride Source = "override" )
The sources. Override is never listed in an order; it is what a Resolution reports when the caller supplied the reference.
func ParseOrder ¶
ParseOrder turns source names into an order, refusing unknown names and duplicates. It does not accept "none": an empty order is the caller's way to skip resolution.