snapshot

package
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Nov 2, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package snapshot stores captured responses: one record per request plus the body, kept by content. Public bodies sit next to the records; private bodies (of captures and baselines alike) are encrypted in the project's local store and never enter git, not even encrypted: a record names its private body by a keyed hash only.

Index

Constants

View Source
const (
	Public  = "public"
	Private = "private"
)

Storage of a body.

View Source
const TempPrefix = ".tmp-"

TempPrefix starts the name of a file being written (WriteFile).

View Source
const Version = 1

Variables

View Source
var ErrPlainHash = errors.New("private body named by its plain SHA-256 (made before keyed refs)")

ErrPlainHash means a private record names its body by the plain SHA-256 of the content, as records did before keyed refs; such a body is only in the local store of the machine that captured it.

View Source
var ErrPrivateMissing = errors.New("private body not found")

ErrPrivateMissing means a private body is not in its store.

Functions

func Each

func Each(n int, do func(i int) error) error

Each runs do for 0…n-1 on one goroutine per CPU, at most eachWorkers. After an error no new item starts; the error returned is the one of the lowest i that failed, as when the items run one after another.

func FoldName

func FoldName(name string) string

FoldName is the form under which two names are the same file on a file system that ignores case.

func RemoveStale

func RemoveStale(dir string, before time.Time)

RemoveStale removes the files a write left behind when its process was killed (TempPrefix, up to MaxBody each), under dir, that are older than before; the folder dir itself is left out (another process may be writing a file of its own there). The caller holds the store's lock, so no other process is writing below it.

func SimpleName

func SimpleName(name string) bool

SimpleName reports a name that can name a file or folder on every system DriftGate runs on: letters, digits and . _ - (not first), not ending in a dot (Windows drops it, so "a." would be "a"), and not a device name Windows reserves (con, nul, com1…, also as con.x). Names that differ only in case are the same file on macOS and Windows; callers that keep several names side by side check that with FoldName.

func Sum

func Sum(b []byte) string

Sum is the hex SHA-256 of b.

func WriteFile

func WriteFile(path string, data []byte, perm os.FileMode) error

WriteFile writes atomically: a temporary file in the same directory is renamed over the target, so an interrupted run never leaves half a file.

func WriteJSON

func WriteJSON(path string, v any) error

WriteJSON writes v with stable formatting (one-space indent, trailing newline).

Types

type Body

type Body struct {
	SHA256  string `json:"sha256"` // public bodies; empty for a private body with a ref
	Ref     string `json:"ref,omitempty"`
	Storage string `json:"storage"`
	Size    int64  `json:"size"`
}

Body describes a response body without holding it. A public body is named by the SHA-256 of its content; a private one by its Ref, a keyed hash, so that nothing committed holds a plain hash of personal data.

func (Body) PlainHash

func (b Body) PlainHash() bool

PlainHash reports whether a private body is named by the plain SHA-256 of its content: a record made before refs, whose body only the local store of the machine that captured it may hold.

type Concurrency

type Concurrency struct {
	Service string `json:"service,omitempty"` // empty: the only service
	Value   int    `json:"value"`
	Reason  string `json:"reason"`
	// Probe is how long the first read-only requests took at each setting
	// tried.
	Probe []Probe `json:"probe,omitempty"`
	// Tried is the setting compared with a sequential capture of the same
	// state; SequentialMS and ParallelMS are the two captures' times.
	Tried        int   `json:"tried,omitempty"`
	SequentialMS int64 `json:"sequential_ms,omitempty"`
	ParallelMS   int64 `json:"parallel_ms,omitempty"`
	// Differed lists, sorted, the requests or state items that differed
	// from the sequential capture, the reason for one at a time.
	Differed []string `json:"differed,omitempty"`
}

Concurrency is the decision of concurrency: auto for one service, with its evidence.

type Meta

type Meta struct {
	Version int    `json:"version"`
	Tag     string `json:"tag"`
	Source  string `json:"source"`
	Created string `json:"created"`
	// State is the fingerprint of the state the capture started from, per
	// component; a capture of another state cannot be compared with it.
	State map[string]map[string]string `json:"state,omitempty"`
	// StateFormat is the state.FingerprintFormat of State; 0 before v1.3.
	StateFormat int `json:"state_format,omitempty"`
	// Unreliable marks a capture made with --skip-fingerprint-check.
	Unreliable bool `json:"unreliable,omitempty"`
	// Incomplete marks a capture that has not finished: it is written
	// first and replaced at the end, so an interrupted capture is not
	// taken for one whose requests are missing.
	Incomplete bool `json:"incomplete,omitempty"`
	// Concurrency is what concurrency: auto decided for each service, in
	// name order; later captures use it without measuring again.
	Concurrency []Concurrency `json:"concurrency,omitempty"`
	// Origins are the origins (scheme://host[:port]) the requests were sent
	// to, per service (the empty name: the only one). Responses that write
	// absolute URLs are compared with another origin's through them.
	Origins map[string]string `json:"origins,omitempty"`
}

Meta describes a snapshot set.

type PrivateStore

type PrivateStore struct {
	Dir string
	Ext string // ".enc" in the local store; none in the private/ folder of an older baseline
	Key func() (secret.Key, error)
	// KeyHint, when set, says where the key came from, for a body it
	// cannot open.
	KeyHint func() string
}

PrivateStore keeps encrypted private bodies, named by their Ref (the keyed hash of the plaintext): the local store as <ref>.enc. Every file is sealed with its name, so a file renamed or moved to another body does not decrypt. The key is loaded only when a body is actually read or written.

func (*PrivateStore) Get

func (p *PrivateStore) Get(ref string) ([]byte, error)

Get decrypts a body and checks that it is the body its ref names.

func (*PrivateStore) GetPlain

func (p *PrivateStore) GetPlain(sum string) ([]byte, error)

GetPlain decrypts a body stored under the plain SHA-256 of its content, as the local store kept them before refs; a body that is not there returns nil without error.

func (*PrivateStore) Has

func (p *PrivateStore) Has(ref string) bool

Has reports whether the encrypted body exists.

func (*PrivateStore) Put

func (p *PrivateStore) Put(body []byte) (string, error)

Put encrypts and stores a body and returns its ref.

func (*PrivateStore) PutInPlace

func (p *PrivateStore) PutInPlace(body []byte) (string, error)

PutInPlace is Put that encrypts body in its own storage, so a large body is not held twice: body is overwritten and must not be used afterwards.

type Probe

type Probe struct {
	Concurrency int   `json:"concurrency"`
	Requests    int   `json:"requests"`
	MS          int64 `json:"ms"`
}

Probe is one setting of the throughput probe.

type Record

type Record struct {
	Version int         `json:"version"`
	ID      string      `json:"id"`
	Status  int         `json:"status"`
	Headers [][2]string `json:"headers"` // names lower-case, sorted by name; values in received order
	Body    Body        `json:"body"`
	Error   string      `json:"error,omitempty"` // the request failed; no status or body
	// SideEffects is set when the capture tracked the state: what the request
	// changed in the data (an empty list when nothing).
	SideEffects *SideEffects `json:"side_effects,omitempty"`
	// Repeat lists the statuses of a request sent several times, in order
	// ("200×60, 429×1"); the record is the last response.
	Repeat string `json:"repeat,omitempty"`
	// Exit is the exit code of a command step; its body is the output, when
	// the step compares it.
	Exit *int `json:"exit,omitempty"`
}

Record is one captured response.

type RowChange

type RowChange struct {
	Item    string   `json:"item"` // <component>/table:<table>
	Key     string   `json:"key"`  // primary key as column=value pairs
	Change  string   `json:"change"`
	Columns []string `json:"columns,omitempty"` // for an update: the columns that changed
}

RowChange is one changed row of a tracked table.

type Set

type Set struct {
	Dir     string
	Private *PrivateStore
	// contains filtered or unexported fields
}

Set is a directory of records (responses/<id>.json) and public bodies (bodies/<sha256>). Its private bodies are in the local store.

func (*Set) Body

func (s *Set) Body(r *Record) ([]byte, error)

Body returns the body of a record. A private body that is not available returns ErrPrivateMissing, or ErrPlainHash for a record made before refs.

func (*Set) CacheRecords

func (s *Set) CacheRecords()

CacheRecords makes the set keep every record it reads, for a command that reads a record more than once and writes none. A cached record is shared: callers must not change it.

func (*Set) Exists

func (s *Set) Exists() bool

Exists reports whether the set has been written.

func (*Set) Meta

func (s *Set) Meta() (*Meta, error)

Meta reads meta.json; a set without one returns nil without error.

func (*Set) Record

func (s *Set) Record(id string) (*Record, error)

Record reads one record; a missing record returns nil without error.

func (*Set) Records

func (s *Set) Records() ([]*Record, error)

Records reads every record of the set, sorted by ID. They are read in parallel; the error is the one of the first ID that failed.

func (*Set) Write

func (s *Set) Write(r *Record, body []byte) error

Write stores a record and its body. A nil body keeps the record as it is (a private body whose raw content is not available keeps only its name).

func (*Set) WriteMeta

func (s *Set) WriteMeta(m Meta) error

WriteMeta stores the set's description. A description that marks the set complete is handed to the disk before WriteMeta returns (the file and its folder are synced, syncFile), so a crash of the system does not leave a set whose description is lost or empty; the records and bodies are not synced one by one, which would cost a capture more than its requests.

func (*Set) WriteOwned

func (s *Set) WriteOwned(r *Record, body []byte) error

WriteOwned is Write for a body nothing reads afterwards (a capture's response): a private one is encrypted in its own storage, so a large body is not held twice, and body must not be used after the call.

type SideEffects

type SideEffects struct {
	Items []string    `json:"items"`          // <component>/<item>, e.g. db/table:faq
	Rows  []RowChange `json:"rows,omitempty"` // rows of tables under row_tracking
}

SideEffects are the changes one request made to the state.

Jump to

Keyboard shortcuts

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