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).
121 lines
4.1 KiB
Go
121 lines
4.1 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// What the mesh does when a module's build moves (novox/hq ADR 0235, extending ADR 0162 §3 and ADR
|
|
// 0218 §2).
|
|
//
|
|
// **Rolled out by default, one machine first and gated.** With the gate on the first machine and the
|
|
// rollback after it (to-be 45 §8), a build that moves is sent to one machine, judged there by its own
|
|
// health, and only then to the rest — or put back there, said, and sent nowhere else. Recording an
|
|
// upgrade and waiting for a person to push it is kept where a module says why, and where the mesh knows
|
|
// a rollback cannot undo what a new build does.
|
|
|
|
// The policies a module may declare.
|
|
const (
|
|
// PolicyRoll sends one machine first, judges it at the gate, then the rest.
|
|
PolicyRoll = "roll"
|
|
// PolicyTogether sends every machine running the module at once — for a module that must change
|
|
// everywhere in the same minute. Still judged, on every machine, after.
|
|
PolicyTogether = "together"
|
|
// PolicyRecord builds and sends nothing: the machines running it are behind until a person pushes.
|
|
PolicyRecord = "record"
|
|
)
|
|
|
|
// UpgradePolicy is what a module says about how its new builds reach its machines: `upgrade` in its
|
|
// manifest.
|
|
type UpgradePolicy struct {
|
|
Policy string `json:"policy"`
|
|
// Why is required for anything but roll: a person reading the catalogue sees why this module waits
|
|
// for them, or why it changes everywhere at once.
|
|
Why string `json:"why,omitempty"`
|
|
}
|
|
|
|
func (m Manifest) upgradeProblems() []string {
|
|
if m.Upgrade == nil {
|
|
return nil
|
|
}
|
|
switch m.Upgrade.Policy {
|
|
case PolicyRoll:
|
|
case PolicyTogether, PolicyRecord:
|
|
if strings.TrimSpace(m.Upgrade.Why) == "" {
|
|
return []string{fmt.Sprintf("upgrade %q says why: a module that does not roll out one machine first "+
|
|
"names the reason a person reads", m.Upgrade.Policy)}
|
|
}
|
|
default:
|
|
return []string{fmt.Sprintf("upgrade is %q, %q or %q, not %q", PolicyRoll, PolicyTogether, PolicyRecord,
|
|
m.Upgrade.Policy)}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Where a policy came from, as `upgrade` says it.
|
|
const (
|
|
FromPerson = "person"
|
|
FromModule = "module"
|
|
FromBus = "the bus"
|
|
FromData = "irreplaceable data"
|
|
FromDefault = "default"
|
|
)
|
|
|
|
// DerivedUpgrade is the policy a module's manifest gives it when no person has chosen one, with where
|
|
// it came from and why (ADR 0235):
|
|
//
|
|
// - **the bus is never rolled**: a module that provides the mesh's bus records, whatever it says — its
|
|
// upgrade is a planned step a person starts (to-be 45 §8);
|
|
// - a module that says its policy has it;
|
|
// - a module that keeps irreplaceable data records — sending the previous build cannot undo what a new
|
|
// one did to data that cannot be had again, so a person takes it, after a backup;
|
|
// - everything else rolls out, one machine first.
|
|
func DerivedUpgrade(m Manifest) (policy, from, why string) {
|
|
if ProvidesBus(m) {
|
|
return PolicyRecord, FromBus, "the bus is replaced only as a planned step a person starts (`bus upgrade`): " +
|
|
"its streams are snapshotted first and checked after"
|
|
}
|
|
if m.Upgrade != nil && m.Upgrade.Policy != "" {
|
|
return m.Upgrade.Policy, FromModule, m.Upgrade.Why
|
|
}
|
|
if item := m.irreplaceable(); item != "" {
|
|
return PolicyRecord, FromData, "it keeps irreplaceable data (" + item + "): a rollback cannot undo what a new " +
|
|
"build does to it, so a person takes each build, after a backup"
|
|
}
|
|
return PolicyRoll, FromDefault, ""
|
|
}
|
|
|
|
// ProvidesBus is whether a manifest provides the mesh's bus.
|
|
func ProvidesBus(m Manifest) bool {
|
|
for _, o := range m.Provides {
|
|
if o.Name == "mesh-bus" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// irreplaceable names the first irreplaceable data the module keeps, its own or its consumers', or
|
|
// nothing.
|
|
func (m Manifest) irreplaceable() string {
|
|
if m.Data == nil {
|
|
return ""
|
|
}
|
|
for _, it := range m.Data.Own {
|
|
if it.Class == ClassIrreplaceable {
|
|
return it.ID
|
|
}
|
|
}
|
|
for provision, c := range m.Data.Consumers {
|
|
if c.Class == ClassIrreplaceable {
|
|
return "its consumers' " + provision
|
|
}
|
|
}
|
|
for provision, k := range m.Data.KeptBy {
|
|
if k.Class == ClassIrreplaceable {
|
|
return "what it keeps with " + provision
|
|
}
|
|
}
|
|
return ""
|
|
}
|