verify

package
v1.12.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package verify runs a full fire/restore cycle and checks the result by reading the account back, twice.

WHY THIS EXISTS: the planner, the state machine and the ordering are unit-tested, and discovery has run against live endpoints. But nothing had ever run with credentials that work, so no fire and no restore had ever executed. Unit tests cannot catch an API contract mismatch — a field that means something other than what was assumed, a call that succeeds and does nothing, a restore that puts back a value the API silently coerces — and that is the failure this is guarding against.

THE SECOND READ IS THE WHOLE POINT. `fire` returning no error means the API accepted the calls. It does not mean the desired-count is actually zero, the listener actually blocked, or the concurrency actually pinned. So every action is checked by re-discovering the account and comparing what came back against what the action claimed to do — and after `restore`, against what was there before anything was touched.

discover ──► plan ──► fire ──► DISCOVER AGAIN ──► did each action land?
    │                                                    │
    └── pre-state ◄──────── DISCOVER AGAIN ◄── restore ◄──┘
                                  │
                            is everything back?

The output is deliberately counts and kinds. A verification run happens in somebody's account, and the summary is meant to be pasted into an issue: ARNs, account ids, instance names and DNS names must not travel with it. Redact() is not a formatting nicety, it is the thing that makes the report postable.

Index

Constants

This section is empty.

Variables

AllKinds is what a scratch account has to hold for a run to mean anything. Kept here rather than derived from what was found, so a kind that discovery silently stopped returning shows up as unexercised rather than vanishing.

Functions

This section is empty.

Types

type Discoverer

type Discoverer interface {
	Discover(ctx context.Context) ([]model.Resource, []error)
}

Discoverer reads the account. An interface so the whole cycle runs against an in-memory account in the tests: a harness whose own logic is unverified would be the worst possible thing to trust a fire/restore report to.

type Finding

type Finding struct {
	Stage  string // "fire" or "restore"
	Kind   model.Kind
	Ref    string // "ecs-service#2" — stable within one report, meaningless outside it
	Want   string
	Got    string
	Detail string
}

Finding is one thing that did not match. Every field here is safe to post: the resource is named by kind and by an opaque index, never by ARN.

func (Finding) String

func (f Finding) String() string

type Options

type Options struct {
	Settle    time.Duration
	Poll      time.Duration
	Now       func() time.Time
	SkipFire  bool // plan and report only; nothing is changed
	Region    string
	AccountID string
}

Options tunes how patient the verifier is.

Settle is how long to wait after a change before reading it back. AWS is eventually consistent on most of these — an ECS service reports its old desired count for a moment, an ASG takes a beat to reflect a scale — and a verifier that read too early would report a divergence that is only its own impatience.

type Report

type Report struct {
	StartedAt time.Time
	Duration  time.Duration

	// Discovered counts every resource found, by kind. A kind with zero here
	// is a kind this run did NOT exercise, which is as important as the ones
	// it did — the acceptance criterion is "at least one of each".
	Discovered map[model.Kind]int
	Planned    map[model.Kind]int
	Refused    map[model.Kind]int

	FireChanged   int
	FireFailed    int
	FireVerified  int
	RestoreOK     int
	RestoreFailed int

	Findings []Finding

	// Unexercised names the kinds the scratch account had none of. Listed
	// explicitly, because "no findings" over a kind that was never present is
	// not evidence of anything.
	Unexercised []model.Kind

	Errors []string
}

Report is what gets pasted into the issue.

func Run

func Run(
	ctx context.Context,
	d Discoverer,
	ex engine.Executor,
	st state.Store,
	pol policy.Policy,
	opt Options,
) (*Report, error)

Run does the cycle. It returns a report even on error: a run that fell over halfway is exactly the one whose partial findings are worth reading.

func (*Report) OK

func (rep *Report) OK() bool

OK is whether the run is evidence the tool works: every action landed, every resource came back, and nothing diverged. A run with unexercised kinds is still OK — it is just narrower, and Text() says which.

func (*Report) Text

func (rep *Report) Text() string

Text renders the report as something that can be pasted into an issue.

The redaction is structural, not a filter: nothing in the Report type holds an ARN, an account id or a resource name in the first place, so there is nothing here that could accidentally print one. That is deliberate — a formatter that had access to the identifiers and chose not to print them would be one edit away from leaking an account inventory into a public tracker.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL