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
- Variables
- func Each(n int, do func(i int) error) error
- func FoldName(name string) string
- func RemoveStale(dir string, before time.Time)
- func SimpleName(name string) bool
- func Sum(b []byte) string
- func WriteFile(path string, data []byte, perm os.FileMode) error
- func WriteJSON(path string, v any) error
- type Body
- type Concurrency
- type Meta
- type PrivateStore
- type Probe
- type Record
- type RowChange
- type Set
- func (s *Set) Body(r *Record) ([]byte, error)
- func (s *Set) CacheRecords()
- func (s *Set) Exists() bool
- func (s *Set) Meta() (*Meta, error)
- func (s *Set) Record(id string) (*Record, error)
- func (s *Set) Records() ([]*Record, error)
- func (s *Set) Write(r *Record, body []byte) error
- func (s *Set) WriteMeta(m Meta) error
- func (s *Set) WriteOwned(r *Record, body []byte) error
- type SideEffects
Constants ¶
const ( Public = "public" Private = "private" )
Storage of a body.
const TempPrefix = ".tmp-"
TempPrefix starts the name of a file being written (WriteFile).
const Version = 1
Variables ¶
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.
var ErrPrivateMissing = errors.New("private body not found")
ErrPrivateMissing means a private body is not in its store.
Functions ¶
func Each ¶
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 ¶
FoldName is the form under which two names are the same file on a file system that ignores case.
func RemoveStale ¶
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 ¶
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.
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.
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 ¶
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) Records ¶
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 ¶
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 ¶
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.
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.