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 0236, 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 0236):
|
|
//
|
|
// - **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 ""
|
|
}
|