Answer what the records keep, collect on a person's word, and name a machine's images (hq ADR 0251)
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer delivery to the same trunk took over its walk
The store's tools and a machine's image pruning decide from the records, so the controller says them: artifacts (every recorded artifact, kept and why, eligible, collected on request), collect (the after-build sweep on demand, a dry run unless confirmed with a why, recorded as a hand act) and images (what a machine's declaration names, now and as last sent). The sweep is one implementation with two sets of bounds.
This commit is contained in:
@@ -27,7 +27,39 @@ import (
|
||||
// release that turns out wrong can be taken.
|
||||
const KeptBuilds = 5
|
||||
|
||||
// ToCollect is every artifact the mesh made, no longer keeps, and has not already collected.
|
||||
// Why an artifact is kept: the words `artifacts` answers (novox/hq ADR 0251).
|
||||
const (
|
||||
// KeptByDefinition: a definition the mesh holds names it, so it could be handed to a machine now.
|
||||
KeptByDefinition = "definition"
|
||||
// KeptByRecentBuild: it is an artifact of one of the KeptBuilds most recent successful builds of a
|
||||
// module the mesh still holds — somewhere a release that turns out wrong can go back to.
|
||||
KeptByRecentBuild = "recent-build"
|
||||
// KeptUnaddressable: it names no digest, so nothing here can speak for it, and it is kept rather
|
||||
// than guessed about.
|
||||
KeptUnaddressable = "unaddressable"
|
||||
)
|
||||
|
||||
// The states an artifact the mesh recorded making is in (novox/hq ADR 0251).
|
||||
const (
|
||||
ArtifactKept = "kept"
|
||||
ArtifactEligible = "eligible"
|
||||
ArtifactCollected = "collected"
|
||||
)
|
||||
|
||||
// ArtifactState is one artifact the mesh recorded making, and what the records say of it now.
|
||||
type ArtifactState struct {
|
||||
Reference string
|
||||
State string
|
||||
// Why is the reasons a kept artifact is kept; empty for any other.
|
||||
Why []string
|
||||
}
|
||||
|
||||
// Artifacts is every artifact any successful build recorded, oldest first, each with its state:
|
||||
// kept (and why), eligible to let go, or already collected (novox/hq ADR 0189, ADR 0251).
|
||||
//
|
||||
// **One reading of the records for every question asked of them**: the sweep's ToCollect, the
|
||||
// operator's `artifacts` and the store's own tools read the same states, so what one says may go is
|
||||
// what the others say is eligible.
|
||||
//
|
||||
// Three reasons an artifact stays, and nothing else is a reason:
|
||||
//
|
||||
@@ -38,57 +70,62 @@ const KeptBuilds = 5
|
||||
// nothing beyond what a held definition names (novox/hq issue 253);
|
||||
// - it was already collected, in which case there is nothing left to do.
|
||||
//
|
||||
// Returned in a stated order so two runs over the same records ask for the same things in the
|
||||
// same sequence, which is what makes a failed sweep safe to simply run again.
|
||||
func (i *Inventory) ToCollect(ctx context.Context) ([]string, error) {
|
||||
keep, err := i.keptReferences(ctx)
|
||||
// In a stated order, so two runs over the same records ask for the same things in the same
|
||||
// sequence, which is what makes a failed sweep safe to simply run again.
|
||||
func (i *Inventory) Artifacts(ctx context.Context) ([]ArtifactState, error) {
|
||||
keep, err := i.KeepSet(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
// Every artifact of every successful build, oldest first, minus what has already been
|
||||
// collected. A failed build published nothing, so it names nothing to remove.
|
||||
`select b.made
|
||||
from build b
|
||||
where b.failed = '' and b.module is not null and b.module <> ''
|
||||
order by b.at asc, b.id asc`)
|
||||
all, err := i.everyReferenceMade(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
collected, err := i.alreadyCollected(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
var out []string
|
||||
for rows.Next() {
|
||||
var raw []byte
|
||||
if err := rows.Scan(&raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var made []Artifact
|
||||
if err := json.Unmarshal(raw, &made); err != nil {
|
||||
// One unreadable record must not stop the rest being collected — and an artifact this
|
||||
// row named is simply not offered, which errs toward keeping.
|
||||
continue
|
||||
}
|
||||
for _, a := range made {
|
||||
reference := asRecorded(a.Reference)
|
||||
if reference == "" || keep[reference] || collected[reference] || seen[reference] {
|
||||
continue
|
||||
}
|
||||
seen[reference] = true
|
||||
out = append(out, reference)
|
||||
out := make([]ArtifactState, 0, len(all))
|
||||
for _, reference := range all {
|
||||
switch {
|
||||
case len(keep[reference]) > 0:
|
||||
out = append(out, ArtifactState{Reference: reference, State: ArtifactKept, Why: keep[reference]})
|
||||
case collected[reference]:
|
||||
out = append(out, ArtifactState{Reference: reference, State: ArtifactCollected})
|
||||
default:
|
||||
out = append(out, ArtifactState{Reference: reference, State: ArtifactEligible})
|
||||
}
|
||||
}
|
||||
return out, rows.Err()
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// keptReferences is every artifact reference the mesh still keeps, for either of the two reasons.
|
||||
func (i *Inventory) keptReferences(ctx context.Context) (map[string]bool, error) {
|
||||
keep := map[string]bool{}
|
||||
// ToCollect is every artifact the mesh made, no longer keeps, and has not already collected —
|
||||
// oldest first (Artifacts says why each other one stays).
|
||||
func (i *Inventory) ToCollect(ctx context.Context) ([]string, error) {
|
||||
states, err := i.Artifacts(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []string
|
||||
for _, s := range states {
|
||||
if s.State == ArtifactEligible {
|
||||
out = append(out, s.Reference)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// KeepSet is every artifact reference the mesh still keeps, each with the reasons it is kept.
|
||||
func (i *Inventory) KeepSet(ctx context.Context) (map[string][]string, error) {
|
||||
keep := map[string][]string{}
|
||||
because := func(reference, why string) {
|
||||
for _, w := range keep[reference] {
|
||||
if w == why {
|
||||
return
|
||||
}
|
||||
}
|
||||
keep[reference] = append(keep[reference], why)
|
||||
}
|
||||
|
||||
// **Whatever a definition the mesh holds names.** Read as text rather than by walking the
|
||||
// resource shapes: a reference may be a container's image, a bundle's source, or a field some
|
||||
@@ -140,7 +177,7 @@ func (i *Inventory) keptReferences(ctx context.Context) (map[string]bool, error)
|
||||
}
|
||||
for _, a := range made {
|
||||
if reference := asRecorded(a.Reference); reference != "" {
|
||||
keep[reference] = true
|
||||
because(reference, KeptByRecentBuild)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,28 +185,28 @@ func (i *Inventory) keptReferences(ctx context.Context) (map[string]bool, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// And anything a manifest mentions. Done after the recent set so the scan runs over the
|
||||
// candidates rather than over every reference ever recorded: a manifest holds a reference
|
||||
// composed with the store's address or kept bare, so the search is for the digest within it.
|
||||
// And anything a manifest mentions — every reference ever made, the recent ones included, so a
|
||||
// kept artifact carries each of its reasons and the operator reads why, not only that (novox/hq
|
||||
// ADR 0251). A manifest holds a reference composed with the store's address or kept bare, so the
|
||||
// search is for the digest within it.
|
||||
if len(named) > 0 {
|
||||
all, err := i.everyReferenceMade(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, reference := range all {
|
||||
if keep[reference] {
|
||||
continue
|
||||
}
|
||||
digest := digestIn(reference)
|
||||
if digest == "" {
|
||||
// Not something the store holds by digest; nothing here can speak for it, so it
|
||||
// is kept rather than guessed about.
|
||||
keep[reference] = true
|
||||
if len(keep[reference]) == 0 {
|
||||
because(reference, KeptUnaddressable)
|
||||
}
|
||||
continue
|
||||
}
|
||||
for _, text := range named {
|
||||
if strings.Contains(text, digest) {
|
||||
keep[reference] = true
|
||||
because(reference, KeptByDefinition)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -186,7 +223,7 @@ func (i *Inventory) keptReferences(ctx context.Context) (map[string]bool, error)
|
||||
// reference that is kept because nothing here can speak for it is not one the store can be asked
|
||||
// about.
|
||||
func (i *Inventory) KeptArchives(ctx context.Context) ([]string, error) {
|
||||
keep, err := i.keptReferences(ctx)
|
||||
keep, err := i.KeepSet(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -200,10 +237,11 @@ func (i *Inventory) KeptArchives(ctx context.Context) ([]string, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// everyReferenceMade is every artifact reference any successful build recorded.
|
||||
// everyReferenceMade is every artifact reference any successful build recorded, oldest build first.
|
||||
func (i *Inventory) everyReferenceMade(ctx context.Context) ([]string, error) {
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select made from build where failed = '' and module is not null and module <> ''`)
|
||||
`select made from build where failed = '' and module is not null and module <> ''
|
||||
order by at asc, id asc`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user