Archived
Gate a release plan's first machine and roll a failed build back there (hq ADR 0236)
A build that reported applied was sent everywhere; one that then did nothing, served no tools or broke its machine's word reached every machine. Now the first machine is judged by the component's health (the core's definitions, as doctor probes H-*, or a module's own) three times over two minutes within ten; a failing gate puts the previous build back there once, marks the build, and says it as a condition and an event. Upgrades roll out by default; the bus is a planned step; a module deleted at its source is not built (the public-acme plan failure).
This commit is contained in:
+120
-19
@@ -1194,15 +1194,62 @@ func (i *Inventory) Catalogued(ctx context.Context) ([]Entry, error) {
|
||||
// providedBy is what the source column says for a module the control plane ships.
|
||||
const providedBy = "the control plane"
|
||||
|
||||
// Upgrade is what the mesh decided to do when a module's current version moves.
|
||||
// Upgrade is what the mesh does when a module's current version moves (ADR 0162 §3, ADR 0235).
|
||||
type Upgrade struct {
|
||||
// RollOut is true when the machines running it should be sent the new version. False means
|
||||
// record it and stop — which needs no record of its own, because a machine not running what
|
||||
// the mesh would send it is already something the mesh reports.
|
||||
// RollOut is true when the machines running it are sent the new version: one machine first, judged
|
||||
// at the gate, then the rest (ADR 0218, ADR 0235). False means record it and stop — the machines
|
||||
// running it are behind until a person pushes, which the mesh already reports.
|
||||
RollOut bool
|
||||
// Together is true when every machine running it is sent the new version at once, rather than
|
||||
// one after another. Only meaningful when RollOut is.
|
||||
// Together is true when every machine running it is sent the new version at once. Only meaningful
|
||||
// when RollOut is.
|
||||
Together bool
|
||||
// From is where the policy came from: a person, the module, the bus, its irreplaceable data, or the
|
||||
// default (catalogue.From*); Why is the reason said with it.
|
||||
From string
|
||||
Why string
|
||||
// By is the person who chose it, when one did.
|
||||
By string
|
||||
}
|
||||
|
||||
// Policy is the policy as a word: roll, together or record.
|
||||
func (u Upgrade) Policy() string {
|
||||
switch {
|
||||
case !u.RollOut:
|
||||
return catalogue.PolicyRecord
|
||||
case u.Together:
|
||||
return catalogue.PolicyTogether
|
||||
}
|
||||
return catalogue.PolicyRoll
|
||||
}
|
||||
|
||||
// upgradeFrom is a module's policy from what the store holds of it: a person's choice, over the module's
|
||||
// own word, over the default — except that the bus is never rolled out, whoever says so (ADR 0235).
|
||||
func upgradeFrom(chosen *string, together bool, why, by string, manifest []byte) Upgrade {
|
||||
var m catalogue.Manifest
|
||||
// Leniently: a policy is read from what was registered, and a manifest registered before a field it
|
||||
// carries was known is still a manifest whose bus and data can be read.
|
||||
_ = json.Unmarshal(manifest, &m)
|
||||
policy, from, said := catalogue.DerivedUpgrade(m)
|
||||
if from != catalogue.FromBus && chosen != nil {
|
||||
policy, from, said = catalogue.PolicyRecord, catalogue.FromPerson, why
|
||||
if *chosen == "roll-out" {
|
||||
policy = catalogue.PolicyRoll
|
||||
if together {
|
||||
policy = catalogue.PolicyTogether
|
||||
}
|
||||
}
|
||||
}
|
||||
u := Upgrade{From: from, Why: said}
|
||||
if from == catalogue.FromPerson {
|
||||
u.By = by
|
||||
}
|
||||
switch policy {
|
||||
case catalogue.PolicyRoll:
|
||||
u.RollOut = true
|
||||
case catalogue.PolicyTogether:
|
||||
u.RollOut, u.Together = true, true
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// UpgradeOf is what to do when this module moves.
|
||||
@@ -1211,30 +1258,80 @@ type Upgrade struct {
|
||||
// mesh has never registered, and being told one of them moved is information, not a fault. The
|
||||
// answer is the safe one — record it — because there is nothing to roll out to.
|
||||
func (i *Inventory) UpgradeOf(ctx context.Context, module string) (Upgrade, error) {
|
||||
var u Upgrade
|
||||
var policy string
|
||||
var chosen *string
|
||||
var together bool
|
||||
var why, by string
|
||||
var manifest []byte
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`select upgrade, upgrade_together from module where name = $1`, module).
|
||||
Scan(&policy, &u.Together)
|
||||
`select upgrade, upgrade_together, upgrade_why, upgrade_by, manifest from module where name = $1`, module).
|
||||
Scan(&chosen, &together, &why, &by, &manifest)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return Upgrade{}, nil
|
||||
return Upgrade{From: catalogue.FromDefault, Why: "the mesh holds no such module"}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return Upgrade{}, err
|
||||
}
|
||||
u.RollOut = policy == "roll-out"
|
||||
return u, nil
|
||||
return upgradeFrom(chosen, together, why, by, manifest), nil
|
||||
}
|
||||
|
||||
// SetUpgradeOf records what to do when this module moves.
|
||||
// Upgrades is every module's policy, by name: what `upgrade` lists.
|
||||
func (i *Inventory) Upgrades(ctx context.Context) (map[string]Upgrade, error) {
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select name, upgrade, upgrade_together, upgrade_why, upgrade_by, manifest from module`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]Upgrade{}
|
||||
for rows.Next() {
|
||||
var name, why, by string
|
||||
var chosen *string
|
||||
var together bool
|
||||
var manifest []byte
|
||||
if err := rows.Scan(&name, &chosen, &together, &why, &by, &manifest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[name] = upgradeFrom(chosen, together, why, by, manifest)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
// ErrBusIsPlanned is a person asking the bus to be rolled out: its upgrade is a planned step (ADR 0235).
|
||||
var ErrBusIsPlanned = errors.New("the bus is never rolled out: its upgrade is a planned step a person starts " +
|
||||
"with `bus upgrade`, which snapshots its streams first and checks them after")
|
||||
|
||||
// SetUpgradeOf records a person's choice of what to do when this module moves, with why and who. A
|
||||
// record says why; the bus is refused a roll-out.
|
||||
func (i *Inventory) SetUpgradeOf(ctx context.Context, module string, u Upgrade) error {
|
||||
policy := "record"
|
||||
if u.RollOut {
|
||||
policy = "roll-out"
|
||||
var manifest []byte
|
||||
err := i.store.Pool().QueryRow(ctx, `select manifest from module where name = $1`, module).Scan(&manifest)
|
||||
if err == nil {
|
||||
var m catalogue.Manifest
|
||||
if json.Unmarshal(manifest, &m) == nil && catalogue.ProvidesBus(m) {
|
||||
return fmt.Errorf("%s provides the mesh's bus: %w", module, ErrBusIsPlanned)
|
||||
}
|
||||
}
|
||||
}
|
||||
tag, err := i.store.Pool().Exec(ctx,
|
||||
`update module set upgrade = $2, upgrade_together = $3 where name = $1`,
|
||||
module, policy, u.Together)
|
||||
`update module set upgrade = $2, upgrade_together = $3, upgrade_why = $4, upgrade_by = $5 where name = $1`,
|
||||
module, policy, u.Together, u.Why, u.By)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tag.RowsAffected() == 0 {
|
||||
return fmt.Errorf("this mesh holds no module called %s", module)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearUpgradeOf takes a person's choice back: the module's own word, or the default, decides again.
|
||||
func (i *Inventory) ClearUpgradeOf(ctx context.Context, module string) error {
|
||||
tag, err := i.store.Pool().Exec(ctx,
|
||||
`update module set upgrade = null, upgrade_together = false, upgrade_why = '', upgrade_by = '' where name = $1`,
|
||||
module)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1258,18 +1355,22 @@ type CurrentBuild struct {
|
||||
// it carried, and what a push compares a machine's last send against.
|
||||
func (i *Inventory) CurrentBuilds(ctx context.Context) (map[string]CurrentBuild, error) {
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select name, coalesce(built_from, ''), upgrade = 'roll-out' from module`)
|
||||
`select name, coalesce(built_from, ''), upgrade, upgrade_together, upgrade_why, upgrade_by, manifest from module`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]CurrentBuild{}
|
||||
for rows.Next() {
|
||||
var name string
|
||||
var name, why, by string
|
||||
var chosen *string
|
||||
var together bool
|
||||
var manifest []byte
|
||||
var b CurrentBuild
|
||||
if err := rows.Scan(&name, &b.Commit, &b.RollOut); err != nil {
|
||||
if err := rows.Scan(&name, &b.Commit, &chosen, &together, &why, &by, &manifest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.RollOut = upgradeFrom(chosen, together, why, by, manifest).RollOut
|
||||
out[name] = b
|
||||
}
|
||||
return out, rows.Err()
|
||||
|
||||
Reference in New Issue
Block a user