Delivery reference: health checks¶
Alpha
Delivery is new: its settings and commands may still change.
Beyond the Pacto contract check, a Stage can check a version's errors and response times in your monitoring, an AI agent's evals and that the service calls only what its contract declares (its claims). Every check answers Pass, Fail or Unknown and cites what it read, so anyone can run it again. An Unknown check holds the change for a person, who can accept the unknown.
The Pacto resource ships inside every version and names its own target, so
pacto.contract and pacto.runtime count only a Pacto that evaluated the
workload the Stage says the service runs: spec.workload (kind and name
in the Stage's namespace), or the Deployment named spec.service.name when
it is empty. A Pacto that checked another workload, a decoy a version shipped
for example, is Unknown. pacto init --delivery writes spec.workload.
Add checks to a Stage¶
List the checks under spec.gates.verify.checks. Each names a provider:
apiVersion: delivery.pacto.trianalab.io/v1alpha1
kind: Stage
metadata:
name: payments
namespace: prod
spec:
service: {name: payments}
environment: prod
applyRef: {name: payments}
candidates:
upstreamStage: {name: payments, namespace: test}
startLevel: advise
gates:
verify:
window: 5m
recheck: 1m
checks:
- name: health
provider: mira
params: {minSpans: "50", maxErrorRatio: "0.02", soak: "3m"}
- name: claims
provider: pacto.runtime
The checks run on the time since Flux applied the version, all at once, and
one that takes longer than a minute is Unknown. The promoter works on
delivery.promoter.workers (4 by default) operations and Stages at a time, so a slow
provider does not hold up the others. Any Fail rolls it
back, all Pass verifies it and anything else waits; when window runs out a
person is asked to accept the unknown or reject the version. While that
question waits, the checks run again every recheck: a Fail still rolls the
version back and all Pass verifies it without the person.
Providers¶
pacto.runtime is built in. Every other provider is configured in the
operator chart's delivery section, so a Stage or an agent can name a provider
and only the chart sets the host it calls:
The URL must be https. A verdict ends up in a signed record, so an answer
forged on the way could get a Pass signed. Plain http works only on loopback,
or with allowPlainHTTP: true on a test cluster. The promoter never follows a
redirect: a provider that answers one gets Unknown.
A provider inside the cluster often has a certificate from an internal CA.
Put that CA's PEM certificates in a ConfigMap in delivery.namespace and name
it, so the promoter trusts it on top of the public roots:
Mira¶
A mira provider counts the service's spans in Mira
by service.name, one span kind at a time: server spans by default, so one
span per request served. Its params:
| Param | Default | Meaning |
|---|---|---|
spanKind |
server |
The spans counted: server, consumer, producer, client, internal, unspecified or any |
minSpans |
20 | Fewer spans than this is Unknown |
maxErrorRatio |
0.05 | A larger share of failed spans fails |
soak |
1m | A clean result passes only after this long |
latencyThreshold |
off | Spans slower than this count as slow |
maxLatencyRatio |
0.05 | A larger share of slow spans fails |
where |
none | Extra Mira terms as JSON, for example the namespace |
matchVersion |
false |
Count only spans whose service.version is the contract's service.version |
By default the provider counts every span of the service in the window, so
while a rollout is still replacing pods the old version's spans count too:
they can hide a breach or fail a clean version. The window starts when Flux or
Argo CD reports the version applied, not when the rollout finished. Set
matchVersion: "true" to judge only the version under check, once the service
sets the service.version resource attribute to its contract's
service.version (for example through OTEL_RESOURCE_ATTRIBUTES). Without a
contract version to match, the check is Unknown.
A read that fails or stops short is Unknown. Mira's query API has no count, so
the provider pages through the spans and stops at 200,000 per query. A window
holding more is judged on its newest soak only, and the message says so; if
even that holds more, the check is Unknown, so narrow it with where. The
citation holds the exact queries with absolute time bounds, so posting them to
Mira again reads the same spans.
What a verdict can prove¶
Mira and http verdicts are unsigned and, by default, self-reported. Mira
counts the spans the service sends about itself, keyed on the service.name
and service.version the sender sets. The version under check, or any pod
that can reach the OTLP endpoint, can leave out its errors or send clean
spans to force a Pass, or send failed ones to force a Fail and a rollback.
An http provider's answer is believed as it arrives over https. To make a
verdict worth more:
- accept telemetry only from authenticated senders at the collector;
- add a
whereon attributes the collector sets, not the sender, such as thek8s.pod.uidork8s.namespace.namethek8sattributesprocessor adds; - count the spans of the service's callers (
spanKind: clienton the caller's side) instead of the service's own.
HTTP providers¶
An http provider is the plug-in format for any other tool. The promoter
posts {"input": ..., "params": ...}, where the input names the operation,
the Stage, the service, the digests under check and the time window. The tool
answers {"subject": "sha256:...", "result": "Pass|Fail|Unknown", "message": "...", "citation": {...}},
where subject is the digest it judged. An answer that names no subject, or
a version other than the one under check, is Unknown.
Eval providers¶
An eval provider runs an AI agent's eval suite that the version ships. See
Delivery reference: eval providers.
Claims against the running service¶
pacto.runtime uses the node agent. It fails a
version the kernel saw calling a Pacto-managed service its contract never
declares, or not listening on a port it declares. It reads the Pacto
evaluation of exactly this version and passes only when
status.evaluatedRevision.kernelCoverage is Complete and the version has run
for soak (a Go duration, default 1m); otherwise the answer is Unknown. A Pass
means only that the node agent saw neither of those two things: right after a
rollout the version may not have handled traffic yet, which is what the soak
is for. A contradiction the kernel saw fails at once.
After release¶
After release the checks run again every recheck (default 1m) on the last
window. A confirmed Fail marks the version failed and raises one rollback
citing the check. The Stage freezes instead when that cause already had its
rollback, when there is nothing to roll back to or when a person or an agent
chose the running version (a rollback, or a promote during auto-hold). It also
freezes, naming the operation, when an operation Pacto did not raise holds the
name of the rollback or freeze it raises (<operation>-rollback,
<operation>-freeze).
Unknown after release never rolls back, and a result about a version the Stage
stopped running is dropped. The newest result is in status.lastCheck, which
also says when the operation that shipped the running version is gone. The
Stage's Watched condition is True while that result passed. It turns
False with reason RecheckUnknown while nothing watches the running version
(a provider down, too many spans, no coverage) and RecheckFailed after a
Fail. The promoter emits an Event when it turns False, and the
pacto_delivery_stage_watched gauge drops to 0, so you can alert on it.
Explain¶
explain shows where an operation is and every verdict with its citation and
how to run it again. --cause-verdict on propose cites a
verdict as its cause, for example on a frozen Stage; the promoter runs it again
and refuses a cause that comes back with another result.