dryrun

package
v0.5.0-beta.3 Latest Latest
Warning

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

Go to latest
Published: Sep 25, 2026 License: PostgreSQL Imports: 8 Imported by: 0

Documentation

Overview

Package dryrun records what a mutating command WOULD have sent, and stops it from being sent.

It is a transport rather than a check at each call site because the CLI builds its HTTP client in two places but writes from dozens of verbs, and because the reported request is then the bytes the generated client really assembled, not a preview that could drift.

The Run is the channel, not the error. RoundTrip must return an error to abort — a synthetic response would be parsed as a real result — but the error cannot be relied on to survive: controlplane's networkError formats its cause with %v into an ExitError with no Unwrap. Callers ask the Run whether an interception happened.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Wrap

func Wrap(base http.RoundTripper, run *Run,
	mutatingGETs ...*regexp.Regexp) http.RoundTripper

Wrap returns base wrapped so that any request that changes state is recorded into run and never sent.

mutatingGETs are path patterns for GET requests that change state anyway: Control Plane declares `GET /v1/cluster/init` and `GET /v1/databases/{id}/tasks/{id}/cancel`, so a method-only rule would let `task cancel --dry-run` really cancel. Each caller supplies the patterns for the spec it vendors; starfleet's three specs have none.

With a nil run it returns base itself, as httplog.Wrap does, so a normal command gets no extra layer.

Types

type Request

type Request struct {
	Method string
	// URL comes from url.URL.Redacted, so an --api-url carrying
	// userinfo credentials cannot be echoed back out of the report.
	URL    string
	Header http.Header
	// Body has been through httplog.RedactBody, so secret values are
	// masked and the length is bounded; it is never the raw payload.
	Body []byte
	// BodyIsJSON reports whether Body still parses as JSON. It is
	// false for a body that never was JSON, one truncated at
	// httplog.MaxDumpBytes, and RedactBody's fail-closed prose summary.
	BodyIsJSON bool
}

Request is the write that would have been sent.

type Run

type Run struct {
	// contains filtered or unexported fields
}

Run is one dry run's state: the checks that passed, and the write that was stopped.

A nil *Run means dry-run is off, and every method tolerates one, so check sites call Pass unconditionally rather than each guarding on a mode flag.

The mutex is needed: the --wait paths issue requests from more than one goroutine.

func New

func New() *Run

New returns an active Run.

func (*Run) Checks

func (r *Run) Checks() []string

Checks returns a copy of the ledger, in recorded order: a goroutine still unwinding may write to the Run while the renderer reads it.

func (*Run) Intercepted

func (r *Run) Intercepted() bool

Intercepted reports whether a write was stopped. A true here is the command's outcome, whatever error the caller also received.

func (*Run) Pass

func (r *Run) Pass(format string, args ...any)

Pass records that a client-side check proved something. Callers state what was PROVED, in the past tense, naming the concrete subject: "database 7f3a5c1e-0000-4000-8000-000000000000 resolved" rather than "checked the name".

func (*Run) Request

func (r *Run) Request() *Request

Request returns the intercepted write, or nil if none happened.

type Transport

type Transport struct {
	Base http.RoundTripper
	Run  *Run

	// MutatingGETs match the PATH of a GET request that changes state.
	// See Wrap.
	MutatingGETs []*regexp.Regexp
}

Transport intercepts writes. Use Wrap rather than constructing one directly, so the off case stays uniform.

func (*Transport) RoundTrip

func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip sends requests that only read and stops everything else.

Jump to

Keyboard shortcuts

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