Files
mesh-controller/internal/catalogue/upgrade.go
T

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 ""
}