Documentation
¶
Overview ¶
Package scenario is the canonical declarative description of a Pacto acceptance fixture.
ONE scenario, several surfaces. The operational-graph vertical is described by a shell harness that publishes contract bundles and mounts an observation export, by a Go gate that proves the resulting Product facts, and by a browser suite that drives the same entities. Before this package the scenario was declared three times over — heredocs in the harness, flags on the gate, an ad-hoc handoff to the browser — and the copies could disagree silently: a version bumped in a heredoc surfaced only as the gate timing out on a revision nobody had published under that name.
The scenario is DATA here, and each surface is a projection of it:
Materialize the contract bundle directories the harness publishes TraceExport the OTLP export the operator mounts as an observation source Plan the machine-readable execution plan both harnesses consume PactoCRs the cluster projection of the deployed targets, once digests exist EvidencePayloads the EvidenceSet each declared envelope carries HelmValues the chart values that configure the Kubernetes surface Compose the Docker Compose surface, distributed as an OCI artifact FactCount how many Product facts this scenario obliges the gate to prove
The expected Product facts are not a separate document: they ARE the scenario. A declared revision must be one canonical retrievable revision, a deployed one must have exactly one operational target linking to it, a relationship with an ObservedBy must be declared, observed and reconciled. The gate walks the value below; nothing about the fixture is written down twice.
Journey inputs are the one thing that cannot be declared: a ServiceKey is domain-escaped and a RevisionKey carries a content id, so the browser suite is handed the keys the gate DISCOVERED, not keys anybody constructed. Journey names which service plays which part, so that handoff is still a projection of this value rather than a fourth declaration of the fixture.
Bundle CONTENT stays literal. A fixture contract is meant to be read, and a generator for it would be a second, untested implementation of the contract schema. The declared identity beside it is proved to agree with the literal by this package's tests instead.
Only projections with a consumer today exist. HelmValues projects the chart values that come from the SCENARIO and nothing else: the operator image, the insecure registry and the enabled components stay in the harness, because they are properties of the run and have no counterpart on the other surface.
TWO surfaces now, which is what Surface is for. Kubernetes and Compose owe different facts, and the difference is DECLARED as a capability Compose does not provide rather than discovered as a shorter run: nothing there reconciles a Pacto CR, so no operational target is expected, and the gate subtracts exactly those facts and says which capability it skipped. Every other fact is owed on both, and parity_test.go compares the rendered projections to prove it.
Values that cannot exist before execution stay runtime inputs, passed in: the registry address the harness happened to bring up, the digests the registry assigned, forwarded ports and temporary directories. Everything a projection needs beyond those is in this value.
Index ¶
- Constants
- Variables
- func DigestKey(service, version string) string
- func EnvelopeID(scenarioName string, i int) string
- func ObservationConfigMap(sourceID string) string
- type Capability
- type ComposeOptions
- type ComposePort
- type Evidence
- type Journey
- type Relationship
- type Revision
- type Scenario
- func (s Scenario) Compose(opts ComposeOptions) ([]byte, error)
- func (s Scenario) Digests(files map[string]string) (map[string]string, error)
- func (s Scenario) EvidencePayloads(dir, domain string, digests map[string]string) (map[string][]byte, error)
- func (s Scenario) EvidenceSubjects(domain string, digests map[string]string) ([]string, error)
- func (s Scenario) FactCount(surface Surface) int
- func (s Scenario) HelmEvidenceValues(domain string, digests map[string]string) ([]string, error)
- func (s Scenario) HelmValues() ([]string, error)
- func (s Scenario) Materialize(dir, domain string) error
- func (s Scenario) MaterializeFiles(domain string) (map[string]string, error)
- func (s Scenario) PactoCRs(namespace, domain string, digests map[string]string) ([]byte, error)
- func (s Scenario) Plan(dir string) ([]byte, error)
- func (s Scenario) Service(name string) (Service, bool)
- func (s Scenario) SourceID(kind SourceKind) string
- func (s Scenario) TraceExport(source string) ([]byte, error)
- func (s Scenario) Validate() error
- type Service
- type Signer
- type Source
- type SourceKind
- type Surface
- type Workload
Constants ¶
const ( // ComposeRegistryHost is the in-network address of the registry the demo starts. ComposeRegistryHost = "registry:5000" // ComposeDomain is the OCI domain the demo's bundles are published under. ComposeDomain = ComposeRegistryHost + "/demo" // ComposeArtifactMount is the directory every inline fixture config is targeted // into, read-only. It is the only path the demo reads fixture data from, which // is what makes the published compose file — and nothing else — the whole input. ComposeArtifactMount = "/demo" // ComposeEvidenceURL is the in-network address of the Evidence Server. ComposeEvidenceURL = "http://evidence:8686" // ComposePlanFile is the execution plan inside the artifact, in Plan's format. ComposePlanFile = ComposeArtifactMount + "/plan.tsv" // ComposeSeedScript is the static script the one-shot seed runs. ComposeSeedScript = ComposeArtifactMount + "/seed.sh" )
The addresses inside the Compose network. They are part of the projection rather than runtime inputs because the demo brings its own registry up under a name it chooses: unlike Kind, where the harness discovers whatever port the host assigned, nothing here can be different from run to run.
const ( // ComposeSourceURL is the project the artifact is built from. ComposeSourceURL = "https://github.com/trianalab/pacto" // ComposeDocsURL is where the authoritative instructions live. The artifact // cannot carry them: see the extension's own comment. ComposeDocsURL = ComposeSourceURL + "/blob/main/docs/examples/compose-demo.md" )
The provenance a Compose OCI artifact CAN carry. The artifact type is a compose file, so there is no layer to put a README in; `x-` extensions are the native way to attach metadata, and they survive publication verbatim, so `docker compose -f oci://…@sha256:… config` shows a user what they pulled and where the instructions are.
const ( RecordPush = "push" RecordObservation = "observation" RecordWorkload = "workload" RecordSigner = "signer" RecordEvidence = "evidence" )
Plan record kinds. Each is a line: the kind, then its fields, TAB-separated.
push <digest key> <bundle dir> <repository:tag> observation <source id> <configMap name> <file key> <export path> workload <service> <deployment> <service port, 0 when unexposed> signer <producer id> <key id> evidence <subject> <payload path> <sequence> <envelope id>
const ComposeDefaultRegistryImage = "ghcr.io/project-zot/zot-minimal:v2.1.20@sha256:73f26433b341f4a319963f7c5e169858663a10565e4037e71605737daee202ee"
ComposeDefaultRegistryImage is the OCI registry the demo starts, pinned to the MULTI-PLATFORM INDEX digest of zot-minimal.
zot, not CNCF distribution, because the demo's registry is also its EVIDENCE STORE: accepted evidence is published as an OCI 1.1 referrer of the contract revision it reports on, and Pacto refuses oras-go's legacy referrers-tag fallback. `registry:2` and `registry:3` implement no referrers endpoint at all, so the Evidence Server would never pass its own readiness probe and the demo would come up with a permanently unhealthy container. zot-minimal keeps the same port and the same /var/lib/registry, and its "minimal" build carries no CVE-database downloader, so the demo needs nothing from the network once its images are pulled.
The index, not one of its children. An index digest still lets Docker resolve the child matching the host, so the demo stays native on amd64 and arm64 alike with no `platform:` anywhere; a per-architecture MANIFEST digest would pin one of them and emulate it everywhere else. The two are indistinguishable as strings, which is exactly how they get confused — this repository has already paid for that once, in the `kind load` failure documented in docs/maintainers/testing.md, where sha256:46faa9a1… is the amd64 CHILD of this same image. The local acceptance re-derives it: it asserts the pulled registry image is the host's own architecture, which a child digest could not be.
Refreshing it is `crane digest ghcr.io/project-zot/zot-minimal:<tag>` — deliberately a human act, since a new digest is a new demo and the artifact says which one it ran.
const ComposeMinVersion = "2.34.0"
ComposeMinVersion is the oldest Docker Compose that owns this artifact type: 2.34.0 added `docker compose publish` and `-f oci://…`. Declared once, here, because the projection, the release unit, CI and the documented user journey all have to mean the same floor — and a user on an older Compose sees a file it will not load rather than an actionable version error.
Variables ¶
var OperationalGraph = Scenario{ Name: "operational-graph", Journey: Journey{ Provider: "checkout", Consumer: "orders", External: "payments", }, Services: []Service{{ Name: "payments", Repo: "payments", EvidenceOnly: true, Revisions: []Revision{{ Version: "1.0.0", Dir: "payments", Files: map[string]string{ "pacto.yaml": `pactoVersion: "2.0" service: { name: payments, version: "1.0.0" } interfaces: [ { name: api, type: openapi, ref: openapi.yaml, visibility: public } ] workload: service state: { type: stateless, persistence: { scope: local, durability: ephemeral }, dataCriticality: low } `, "openapi.yaml": `openapi: "3.0.0" info: { title: payments, version: "1.0.0" } paths: {} `, }, }}, }, { Name: "checkout", Repo: "checkout", Workload: &Workload{Name: "checkout", Interface: "api", Port: 8080}, Revisions: []Revision{{ Version: "1.0.0", Dir: "checkout-a", Deployed: true, Files: map[string]string{ "pacto.yaml": `pactoVersion: "2.0" service: { name: checkout, version: "1.0.0", owner: { team: commerce, dri: d, contacts: [ { type: email, value: a@e.com, purpose: escalation } ] } } interfaces: [ { name: api, type: openapi, ref: openapi.yaml, visibility: public } ] workload: service state: { type: stateless, persistence: { scope: local, durability: ephemeral }, dataCriticality: low } `, "openapi.yaml": `openapi: "3.0.0" info: { title: checkout, version: "1.0.0" } paths: /checkout: { post: { responses: { "200": { description: ok } } } } /cart: { get: { responses: { "200": { description: ok } } } } `, }, }, { Version: "1.1.0", Dir: "checkout-b", Files: map[string]string{ "pacto.yaml": `pactoVersion: "2.0" service: { name: checkout, version: "1.1.0", owner: { team: commerce, dri: d, contacts: [ { type: email, value: a@e.com, purpose: escalation } ] } } interfaces: [ { name: api, type: openapi, ref: openapi.yaml, visibility: public } ] workload: service state: { type: stateless, persistence: { scope: local, durability: ephemeral }, dataCriticality: low } `, "openapi.yaml": `openapi: "3.0.0" info: { title: checkout, version: "1.1.0" } paths: /checkout: { post: { responses: { "200": { description: ok } } } } `, }, }}, }, { Name: "orders", Repo: "orders", Workload: &Workload{Name: "orders"}, Revisions: []Revision{{ Version: "1.0.0", Dir: "orders", Deployed: true, Files: map[string]string{ "pacto.yaml": `pactoVersion: "2.0" service: { name: orders, version: "1.0.0", owner: { team: commerce, dri: d, contacts: [ { type: email, value: a@e.com, purpose: escalation } ] } } workload: service state: { type: stateless, persistence: { scope: local, durability: ephemeral }, dataCriticality: low } dependencies: [ { name: checkout, ref: 'oci://{{.Domain}}/checkout', required: false, compatibility: '^1.0.0' } ] `, }, }}, }}, Sources: []Source{ {ID: "oci", Kind: SourceRegistry}, {ID: "cache", Kind: SourceCache}, {ID: "orders-traces", Kind: SourceObservation}, {ID: "evidence-http", Kind: SourceEvidence}, }, Relationships: []Relationship{{ From: "orders", To: "checkout", Declared: true, ObservedBy: "orders-traces", Reconciliation: "matched", }}, Evidence: []Evidence{{ Service: "payments", Source: "remote-eu", Signer: Signer{Producer: "remote-eu-collector", KeyID: "demo"}, ObservedAt: "2026-07-29T12:00:00Z", Via: "evidence-http", }}, }
OperationalGraph is the fixture the full Pacto vertical is proved against: an operator reconciling real published contract revisions, a dashboard serving them, an offline observation source carrying the matching call, and a signed EvidenceEnvelope arriving from outside the cluster.
Everything downstream reasons about REAL published content, not a synthesized shortcut. Checkout is published twice, differing by exactly one deterministic semantic change, with only the first revision deployed — which is what makes the A -> B change analysis a real question about the fleet rather than a fixture. Orders DECLARES the dependency on checkout, and the observation source carries the call that corroborates it, so declared and observed have to meet in the backend and be reconciled there rather than here.
Functions ¶
func DigestKey ¶
DigestKey names one published revision, in the plan and in the digest map the harness hands back once the registry has assigned one. Service and version, because one artifact is published per version of a service.
func EnvelopeID ¶
EnvelopeID is the id of the nth declared envelope (0-based). Producer-scoped sequence numbers are 1-based and monotonic, so the two are derived together.
func ObservationConfigMap ¶
ObservationConfigMap is the ConfigMap an observation source's export is carried in. Derived from the source id so the ConfigMap the harness creates and the Helm value naming it cannot drift.
Types ¶
type Capability ¶
type Capability string
Capability is something a surface must be able to do for the fixture to be fully provable on it.
const CapabilityOperationalTarget Capability = "operational-target"
CapabilityOperationalTarget is a reconciled runtime target: a controller that resolves a declared contract reference against something actually running and publishes the link. Only Kubernetes has one.
type ComposeOptions ¶
type ComposeOptions struct {
// PactoImage runs the dashboard, the Evidence Server and the seed.
PactoImage string
// RegistryImage runs the OCI registry the demo publishes into.
RegistryImage string
// Version is the release this artifact was built for. It is part of the
// application rather than a label on it: the artifact carries no file a user
// could read a version out of, and two releases whose compose files were
// byte-identical would be one artifact under one digest.
Version string
}
ComposeOptions are the values the Compose projection cannot derive: the images it runs. They are required rather than defaulted because the whole point of the distributed artifact is that it is pinned — a default would produce a demo that meant whatever `latest` meant on the day it was started.
Both must be DIGEST-QUALIFIED. Everything else about the artifact is already immutable — it is published, pulled and documented by digest — so a tag left in here is the one way the same demo artifact can execute different bytes tomorrow than it did today, silently, with its own digest unchanged.
type ComposePort ¶
ComposePort is one published port: which service listens, and the environment variable that moves it on the host.
func ComposePorts ¶
func ComposePorts() []ComposePort
ComposePorts is every port the demo publishes.
One declaration, two consumers: the `ports` entries and the acceptance harness that has to know where to reach the dashboard. The defaults avoid the ports the repository's other harnesses bind, so a demo and a test run can coexist on one machine — and each is overridable by its variable, which is how two versions of the artifact run side by side without colliding.
type Evidence ¶
type Evidence struct {
// Service is the subject of the envelope.
Service string
// Source is the environment the observations were collected in.
Source string
// Signer is the identity that signs the envelope carrying them.
Signer Signer
// ObservedAt is when the remote environment saw it, RFC3339.
ObservedAt string
// Via is the Data Source id the resulting target must be attributed to.
Via string
}
Evidence is a target that arrives from a remote environment over the Evidence Server.
Two identities meet here that a single "producer" field used to conflate, with the harness quietly picking a third value for the one that reaches the wire:
Source WHERE the observations were collected — the environment. It is
payload data: the EvidenceSet's Source and each observation's
collector.
Signer WHO signed the envelope. Producer is the id the envelope claims and
the trust store binds the key to; KeyID selects that key.
They can be the same string and in this fixture they are not, which is the point: the signed envelope now consumes both from here, so neither can be declared and then contradicted.
type Journey ¶
type Journey struct {
// Provider is the service whose two revisions drive change analysis.
Provider string
// Consumer is the service that declares the dependency on Provider.
Consumer string
// External is the service that arrives through the Evidence Server.
External string
}
Journey names the parts the browser suite drives, so the discovered-keys handoff stays a projection of this value.
type Relationship ¶
type Relationship struct {
From, To string
// Declared means the consumer's contract names the dependency.
Declared bool
// ObservedBy is the Data Source id whose export carries the call. Empty means
// the edge is declared but never seen.
ObservedBy string
// Reconciliation is the verdict the backend must reach for the pair, e.g.
// "matched".
Reconciliation string
}
Relationship is an edge between two services.
type Revision ¶
type Revision struct {
Version string
// Dir is the bundle directory Materialize writes, and the directory the
// harness pushes from.
Dir string
// Deployed marks the revision a workload actually runs, which is what makes
// an operational target expected for the service.
Deployed bool
// Files is the bundle content, path relative to Dir. Rendered through
// text/template with .Domain bound to the fixture's OCI domain, so a
// dependency ref can name the registry the harness happens to bring up.
Files map[string]string
}
Revision is one published version of a service.
type Scenario ¶
type Scenario struct {
// Name identifies the scenario in diagnostics.
Name string
// Services are every service the fixture publishes, in a stable order.
Services []Service
// Sources are the Data Sources the Product must publish for this fixture.
Sources []Source
// Relationships are the edges between services, declared and/or observed.
Relationships []Relationship
// Evidence is what arrives from outside the cluster, over the Evidence Server.
Evidence []Evidence
// Journey names the parts the browser suite drives.
Journey Journey
}
Scenario is a fixture the acceptance surfaces share.
func (Scenario) Compose ¶
func (s Scenario) Compose(opts ComposeOptions) ([]byte, error)
Compose renders the Docker Compose projection of the scenario.
func (Scenario) Digests ¶
Digests is the immutable identity every one of the fixture's bundles WILL be published under, computed from the materialized bytes without a registry.
The Kubernetes surface never needs this: a cluster run brings a registry up before it projects anything that has to name a digest, so it uses the real ones the push returned. The Compose surface cannot — its registry does not exist until the artifact has been built, distributed and pulled, and the evidence payloads INSIDE the artifact have to point at content that will exist.
Packing is content-deterministic, so this is the digest the seed's push produces. That is a claim, not an assumption: the seed re-checks each pushed digest against the artifact at run time, and the acceptance harness pushes for real and compares.
Read from MaterializeFiles' bytes rather than from a directory, because the Compose surface — the only caller — never writes one: a Compose OCI artifact carries no files, so there is nothing on disk to read back. Packing canonicalizes every tar header's mode, owner and mtime (pkg/oci), so a bundle packed from these bytes and the same bundle packed from the read-only config mounts the demo's seed pushes from have the same digest.
func (Scenario) EvidencePayloads ¶
func (s Scenario) EvidencePayloads(dir, domain string, digests map[string]string) (map[string][]byte, error)
EvidencePayloads renders the EvidenceSet each declared envelope carries, keyed by the SAME path Plan already told the harness to expect it at — one derivation, so the writer and the reader cannot disagree about where a payload landed.
The ContractRef resolves to the subject's REAL published bundle, so the target the Evidence Server produces points at content that exists in the registry rather than at a plausible-looking string.
func (Scenario) EvidenceSubjects ¶
EvidenceSubjects is the exact set of contract revisions the Evidence Server is configured with: the deduplicated, sorted ContractRefs of every envelope the scenario declares.
One derivation for both surfaces. The registry holding these manifests IS the evidence store — each accepted record is published as an OCI 1.1 referrer of one of them — so a surface configured with a different set would store its evidence somewhere the other surface never looks, and the two demos would stop being one demo. It is exact digests only, never the repository: a subject the server could re-resolve is a subject whose stored evidence could come to describe different content.
func (Scenario) FactCount ¶
FactCount is how many facts the live Product gate is obliged to prove for this scenario ON THE GIVEN SURFACE. It is derived rather than written down so the gate's progress line cannot claim a denominator the fixture stopped justifying.
The surface is a parameter because the two surfaces owe different numbers of facts, and only one reason for that is legitimate: a capability the platform does not have. Compose has no controller, so nothing there can reconcile an operational target, and the target facts are not owed. Every other fact is. An unknown surface provides nothing, so it owes the smallest count — which is why the gate parses its surface rather than accepting a bare string.
func (Scenario) HelmEvidenceValues ¶
HelmEvidenceValues configures the chart's Evidence Server with the scenario's canonical contract subjects, as `evidence.registry.subjects[i]=<ref>` strings.
Separate from HelmValues because it needs the digests, and on Kubernetes those are the REAL ones the push returned rather than the ones computed from bytes: the cluster surface has a registry before it projects anything. Same subjects as the Compose surface all the same — both call EvidenceSubjects — because the registry is the evidence store and two surfaces storing against different revisions would be two different demos.
func (Scenario) HelmValues ¶
HelmValues is the Kubernetes surface's configuration projection: the chart values that come from the SCENARIO, as `key=value` strings a harness turns into `--set` arguments.
It exists because there is now a second consumer. The Kind harness used to build these three keys inline while walking the plan's observation records, which was fine while the cluster was the only surface — but the Compose surface configures the SAME sources under different keys, and the parity test between them has to read what each surface was actually told, not what both were derived from. Two consumers is the rule for a projection existing at all (docs/maintainers/testing.md); this now has them.
What is NOT here is deliberate: the operator image, the insecure registry, the enabled components and the trust Secret are this RUN's values, with one consumer and no counterpart on any other surface. They stay in the harness.
func (Scenario) Materialize ¶
Materialize writes every service's bundle directories under dir, rendering each file against the fixture's OCI domain.
func (Scenario) MaterializeFiles ¶
MaterializeFiles is every bundle document the fixture declares, keyed by its SLASH-SEPARATED path relative to the fixture root and rendered against the fixture's OCI domain.
The bytes, not a directory, because only one of the two surfaces wants a directory. Kubernetes publishes from disk, so Materialize writes these out; the Compose surface distributes a Docker Compose OCI artifact, which carries no files at all and projects each document into the application as an inline config. One derivation either way, so the two surfaces cannot publish different bundles.
func (Scenario) PactoCRs ¶
PactoCRs renders the Pacto custom resources for every service that runs something: the cluster projection of the fixture's operational targets.
It runs after the push because a CR pins the IMMUTABLE digest the registry assigned, which is what makes the operator publish a real resolved contract identity instead of re-resolving a tag. Which revision a CR pins comes from Deployed, so moving the flag moves the applied CR — the shell has no say.
func (Scenario) SourceID ¶
func (s Scenario) SourceID(kind SourceKind) string
SourceID returns the id of the single source playing the given role.
func (Scenario) TraceExport ¶
TraceExport renders the OTLP export the named observation source carries: one client span per relationship that source observes. The export is derived from the same relationships the gate later requires the backend to reconcile, so the observed half of the fixture cannot drift from the declared half.
func (Scenario) Validate ¶
Validate refuses a fixture that cannot mean one thing.
One rule, because one rule is what the surfaces disagreed about: Deployed means "the workload runs THIS". A service that runs something declares exactly one deployed revision; a service that runs nothing declares none.
It is called by the projections and by the Product gate rather than left to DeployedRevision alone, because a surface only asks about the services it projects. Nothing asks a workload-less service what it deploys — so a Deployed flag on one used to sail through every projection untouched while still obliging the gate to find an operational target for a service the cluster had no reason to run.
ponytail: one rule, checked where the fixture is read. Not a validation framework — everything else this fixture must satisfy is already proved by the counterexamples beside it.
type Service ¶
type Service struct {
Name string
// Repo is the repository path under the fixture's OCI domain.
Repo string
// EvidenceOnly marks a service whose bundle is published so a signed
// EvidenceEnvelope can point at real content, but which the Product sees only
// through the Evidence Server: it carries no OCI revisions in the fleet and
// lands in whatever domain the evidence names, not the fixture's.
EvidenceOnly bool
// Workload is what the deployed revision runs as in the cluster. Nil means the
// service publishes revisions but nothing runs them, so no Deployment and no
// Pacto CR are projected for it.
Workload *Workload
Revisions []Revision
}
Service is one service the fixture publishes contract revisions for.
func (Service) DeployedRevision ¶
DeployedRevision returns the ONE revision the service's workload runs.
This used to be a scan returning the first revision flagged Deployed, and every surface ran it independently: a fixture declaring two deployments got a plan, a CR pinning the first and a gate proving that same first one — the second deployment erased, unanimously and in silence. Zero is the same failure from the other side: a CR that pins nothing. Neither is a revision this can return, so both are errors.
Whether the service is deployed AT ALL is Workload, not this. A service that runs nothing has no deployed revision, so asking is the caller skipping its own Workload check, and the honest answer is an error rather than a zero Revision that reads like an answer.
func (Service) PublishedOnlyRevision ¶
PublishedOnlyRevision returns a revision that exists in the registry but runs nowhere — the far side of a change analysis.
type Signer ¶
Signer is a producer identity and the key it signs with. The trust store binds the two: a public key filed under this producer authorizes exactly this producer id, so signing with a producer nobody generated a key for is rejected at ingestion rather than silently accepted.
type Source ¶
type Source struct {
ID string
Kind SourceKind
}
Source is a Data Source identity the Product must publish.
type SourceKind ¶
type SourceKind string
SourceKind is the role a Data Source plays in the fixture.
const ( // SourceRegistry is the OCI registry the fixture publishes to. SourceRegistry SourceKind = "registry" // SourceCache is the dashboard's on-disk OCI cache. SourceCache SourceKind = "cache" // SourceObservation is an operator-managed offline trace export. SourceObservation SourceKind = "observation" // SourceEvidence is the Evidence Server. It is proved by the target it // produced rather than on its own, so it is not counted as a source fact. SourceEvidence SourceKind = "evidence" )
type Surface ¶
type Surface string
Surface is one deployment surface of the canonical scenario.
func ParseSurface ¶
ParseSurface resolves a surface name supplied from outside — a flag, an environment variable — and refuses anything else by name.
func Surfaces ¶
func Surfaces() []Surface
Surfaces returns every declared surface, in a stable order.
func (Surface) Has ¶
func (s Surface) Has(c Capability) bool
Has reports whether s provides the capability.
func (Surface) Missing ¶
func (s Surface) Missing() []Capability
Missing is every capability s does NOT provide, so a gate can name the gap in its own output rather than leaving a shorter run looking like a complete one.
type Workload ¶
type Workload struct {
// Name is the Deployment, and the CR's target workloadRef name.
Name string
// Interface and Port bind one contract interface to the port that serves it.
// A zero Port means the workload is deliberately unexposed: no Service, no
// binding, and the operator's only honest answer about interface availability
// is Unknown.
//
// ponytail: one binding per workload. A second would be a slice; no fixture
// has needed one, and a slice of one is harder to read than a pair.
Interface string
Port int
}
Workload is the cluster side of a deployed service: the Deployment the harness creates and the target the Pacto CR points at. It is declared here so the CR is a projection of this value rather than a YAML heredoc the shell maintains in parallel with it.