Name the decision this builds: hq ADR 0236 (0235 is the bus's snapshot)
This commit is contained in:
@@ -570,7 +570,7 @@ func takeIn(ctx context.Context, inv *inventory.Inventory, result link.BuildResu
|
|||||||
return manifest, kept, fmt.Errorf("%s built %s (%s), and the mesh does not register it: %w",
|
return manifest, kept, fmt.Errorf("%s built %s (%s), and the mesh does not register it: %w",
|
||||||
result.On, result.Repository, short(result.Commit), err)
|
result.On, result.Repository, short(result.Commit), err)
|
||||||
}
|
}
|
||||||
// **A build that failed its gate is never registered again** (novox/hq ADR 0235): an outcome heard
|
// **A build that failed its gate is never registered again** (novox/hq ADR 0236): an outcome heard
|
||||||
// twice, or replayed, would otherwise make the build a rollback put back what the module is again,
|
// twice, or replayed, would otherwise make the build a rollback put back what the module is again,
|
||||||
// and the next push would send it.
|
// and the next push would send it.
|
||||||
if failed, err := inv.GateFailed(ctx, kept.ID); err != nil {
|
if failed, err := inv.GateFailed(ctx, kept.ID); err != nil {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"github.com/novox/mesh-controller/internal/link"
|
"github.com/novox/mesh-controller/internal/link"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The bus as a planned step (novox/hq to-be 45 §8, ADR 0227 rule 8, ADR 0235).
|
// The bus as a planned step (novox/hq to-be 45 §8, ADR 0227 rule 8, ADR 0236).
|
||||||
//
|
//
|
||||||
// **A bus upgrade is never rolled out.** The bus carries every declaration, every report and the
|
// **A bus upgrade is never rolled out.** The bus carries every declaration, every report and the
|
||||||
// controller's own lease; a new bus build that does not come up is a mesh nobody can tell anything,
|
// controller's own lease; a new bus build that does not come up is a mesh nobody can tell anything,
|
||||||
@@ -101,7 +101,7 @@ func pendingBus(ctx context.Context, inv *inventory.Inventory) (busPending, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// busHeld names the machines a push may not send because sending them would replace the bus: the
|
// busHeld names the machines a push may not send because sending them would replace the bus: the
|
||||||
// planned step's, not a push's (ADR 0235). Said with the remedy.
|
// planned step's, not a push's (ADR 0236). Said with the remedy.
|
||||||
func busHeld(ctx context.Context, inv *inventory.Inventory, machines []string) (map[string]string, error) {
|
func busHeld(ctx context.Context, inv *inventory.Inventory, machines []string) (map[string]string, error) {
|
||||||
b, err := pendingBus(ctx, inv)
|
b, err := pendingBus(ctx, inv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -112,7 +112,7 @@ func busHeld(ctx context.Context, inv *inventory.Inventory, machines []string) (
|
|||||||
for _, holder := range b.machines {
|
for _, holder := range b.machines {
|
||||||
if n == holder && b.moves(n) {
|
if n == holder && b.moves(n) {
|
||||||
out[n] = fmt.Sprintf("sending %s would replace the bus (%s %s → %s), which is a planned step: "+
|
out[n] = fmt.Sprintf("sending %s would replace the bus (%s %s → %s), which is a planned step: "+
|
||||||
"`bus upgrade --why …` snapshots its streams first and checks them after (novox/hq ADR 0235)",
|
"`bus upgrade --why …` snapshots its streams first and checks them after (novox/hq ADR 0236)",
|
||||||
n, b.module, short(orNotKnown(b.from[n])), short(b.to))
|
n, b.module, short(orNotKnown(b.from[n])), short(b.to))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,26 +113,26 @@ var probeRegistry = []probe{
|
|||||||
Asks: []broker.SeatVerb{{Seat: "node-backup", Verb: "backed-up"}}},
|
Asks: []broker.SeatVerb{{Seat: "node-backup", Verb: "backed-up"}}},
|
||||||
{ID: "DW", Asserts: "the watchdogs of the signals table ran within three of their intervals",
|
{ID: "DW", Asserts: "the watchdogs of the signals table ran within three of their intervals",
|
||||||
From: "ADR 0227 rule 6: the watchers are watched", Kind: "watchdogs-silent", Phase: 1, run: probeWatchdogs},
|
From: "ADR 0227 rule 6: the watchers are watched", Kind: "watchdogs-silent", Phase: 1, run: probeWatchdogs},
|
||||||
// The core's health definitions (novox/hq to-be 45 §8, ADR 0235): what a core component's new build is
|
// The core's health definitions (novox/hq to-be 45 §8, ADR 0236): what a core component's new build is
|
||||||
// judged by on its first machine, run against every machine between upgrades too.
|
// judged by on its first machine, run against every machine between upgrades too.
|
||||||
{ID: "H-controller", Asserts: "the controller lease is held, renewed in time, by a controller that says it is " +
|
{ID: "H-controller", Asserts: "the controller lease is held, renewed in time, by a controller that says it is " +
|
||||||
"ready: its self-check ran and status answered in full within ten seconds", From: "ADR 0235, to-be 45 §8",
|
"ready: its self-check ran and status answered in full within ten seconds", From: "ADR 0236, to-be 45 §8",
|
||||||
Kind: kindCoreUnhealthy, Phase: 4, run: probeControllerHealth},
|
Kind: kindCoreUnhealthy, Phase: 4, run: probeControllerHealth},
|
||||||
{ID: "H-engine", Asserts: "every machine heard from has reported its current declaration, under a node-engine " +
|
{ID: "H-engine", Asserts: "every machine heard from has reported its current declaration, under a node-engine " +
|
||||||
"build it names", From: "ADR 0235, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4, run: probeEngineHealth},
|
"build it names", From: "ADR 0236, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4, run: probeEngineHealth},
|
||||||
{ID: "H-tools", Asserts: "every machine heard from that runs the node tools has them answering the bus",
|
{ID: "H-tools", Asserts: "every machine heard from that runs the node tools has them answering the bus",
|
||||||
From: "ADR 0235, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4, run: probeToolsHealth},
|
From: "ADR 0236, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4, run: probeToolsHealth},
|
||||||
{ID: "H-bus", Asserts: "every stream and durable consumer the mesh defines is on the bus, and a request crosses " +
|
{ID: "H-bus", Asserts: "every stream and durable consumer the mesh defines is on the bus, and a request crosses " +
|
||||||
"it to the machines' node tools and back", From: "ADR 0235, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4,
|
"it to the machines' node tools and back", From: "ADR 0236, to-be 45 §8", Kind: kindCoreUnhealthy, Phase: 4,
|
||||||
run: probeBusHealth},
|
run: probeBusHealth},
|
||||||
// The gate's verdicts and the witnesses' rollbacks (ADR 0235): each build that failed its gate keeps its
|
// The gate's verdicts and the witnesses' rollbacks (ADR 0236): each build that failed its gate keeps its
|
||||||
// condition until a newer build passes; each rollback a witness stands by is said.
|
// condition until a newer build passes; each rollback a witness stands by is said.
|
||||||
{ID: gateProbe, Asserts: "no build that failed its gate, and no core component a witness put back, goes unsaid; " +
|
{ID: gateProbe, Asserts: "no build that failed its gate, and no core component a witness put back, goes unsaid; " +
|
||||||
"a newer build that passes its gate clears it", From: "ADR 0235, to-be 45 §8", Kind: kindRolledBack,
|
"a newer build that passes its gate clears it", From: "ADR 0236, to-be 45 §8", Kind: kindRolledBack,
|
||||||
Raises: []string{kindRollbackFailed}, Phase: 4, run: probeGates},
|
Raises: []string{kindRollbackFailed}, Phase: 4, run: probeGates},
|
||||||
// The bus's planned step (ADR 0235): open while a person's bus upgrade runs, then checked by H-bus.
|
// The bus's planned step (ADR 0236): open while a person's bus upgrade runs, then checked by H-bus.
|
||||||
{ID: busStepProbe, Asserts: "a bus upgrade a person started is said while it runs, and is followed by the bus's " +
|
{ID: busStepProbe, Asserts: "a bus upgrade a person started is said while it runs, and is followed by the bus's " +
|
||||||
"health within its bound — or is said failed, with its snapshot as the way back", From: "ADR 0235, to-be 45 §8",
|
"health within its bound — or is said failed, with its snapshot as the way back", From: "ADR 0236, to-be 45 §8",
|
||||||
Kind: kindBusMaintenance, Raises: []string{kindBusUpgradeFailed}, Phase: 4, run: probeBusStep},
|
Kind: kindBusMaintenance, Raises: []string{kindBusUpgradeFailed}, Phase: 4, run: probeBusStep},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/novox/mesh-controller/internal/link"
|
"github.com/novox/mesh-controller/internal/link"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The gate on a release plan's first machine, and the rollback after it (novox/hq ADR 0235, to-be 45
|
// The gate on a release plan's first machine, and the rollback after it (novox/hq ADR 0236, to-be 45
|
||||||
// §8, ADR 0227 rule 8).
|
// §8, ADR 0227 rule 8).
|
||||||
//
|
//
|
||||||
// **"Reported applied" is not enough.** ADR 0218 sent a module to one machine first and the rest once
|
// **"Reported applied" is not enough.** ADR 0218 sent a module to one machine first and the rest once
|
||||||
@@ -328,7 +328,7 @@ func gatePassed(ctx context.Context, open *stores, p *inventory.Plan, module str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// carryUserList sends the machine holding the bus the user list a new controller composes, once that
|
// carryUserList sends the machine holding the bus the user list a new controller composes, once that
|
||||||
// controller passed its gate (ADR 0235). The controller's own grants travel in that list, and the old
|
// controller passed its gate (ADR 0236). The controller's own grants travel in that list, and the old
|
||||||
// controller composed the list the plan sent; on 2026-10-06 eight pushes by hand carried a new
|
// controller composed the list the plan sent; on 2026-10-06 eight pushes by hand carried a new
|
||||||
// controller's grant into it. Not when a build its policy or a plan holds back would go with it (ADR
|
// controller's grant into it. Not when a build its policy or a plan holds back would go with it (ADR
|
||||||
// 0221): then it is said, as a push would say it.
|
// 0221): then it is said, as a push would say it.
|
||||||
@@ -457,7 +457,7 @@ func gateFailed(ctx context.Context, open *stores, p *inventory.Plan, module str
|
|||||||
sayRollback(ctx, open, module, g, "")
|
sayRollback(ctx, open, module, g, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// rolledBackEvent is the body of `rolled-back` (ADR 0235): a contract, like a condition's events.
|
// rolledBackEvent is the body of `rolled-back` (ADR 0236): a contract, like a condition's events.
|
||||||
type rolledBackEvent struct {
|
type rolledBackEvent struct {
|
||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
At time.Time `json:"at"`
|
At time.Time `json:"at"`
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
"github.com/novox/mesh-controller/internal/link"
|
"github.com/novox/mesh-controller/internal/link"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The gate on a plan's first machine and the rollback after it (novox/hq ADR 0235, to-be 45 §8).
|
// The gate on a plan's first machine and the rollback after it (novox/hq ADR 0236, to-be 45 §8).
|
||||||
|
|
||||||
// gateMesh is a mesh with `app` running on anchor and laptop at build c1, a newer build c2 registered,
|
// gateMesh is a mesh with `app` running on anchor and laptop at build c1, a newer build c2 registered,
|
||||||
// a plan whose tier built c2, and every send recorded and answered — the machine applies what it is
|
// a plan whose tier built c2, and every send recorded and answered — the machine applies what it is
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ func TestANamedPushLeavesAMachineAPolicyHoldsBack(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Recorded by a person's choice: the default rolls out since novox/hq ADR 0235.
|
// Recorded by a person's choice: the default rolls out since novox/hq ADR 0236.
|
||||||
if err := inv.SetUpgradeOf(ctx, "resolver", inventory.Upgrade{Why: "each machine checked by hand"}); err != nil {
|
if err := inv.SetUpgradeOf(ctx, "resolver", inventory.Upgrade{Why: "each machine checked by hand"}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ func run() error {
|
|||||||
return serve(ctx)
|
return serve(ctx)
|
||||||
case "upgrade":
|
case "upgrade":
|
||||||
return upgradeCommand(ctx, args[1:])
|
return upgradeCommand(ctx, args[1:])
|
||||||
// The bus as a planned step (novox/hq to-be 45 §8, ADR 0235).
|
// The bus as a planned step (novox/hq to-be 45 §8, ADR 0236).
|
||||||
case "bus":
|
case "bus":
|
||||||
return busCommand(ctx, args[1:])
|
return busCommand(ctx, args[1:])
|
||||||
case "declare":
|
case "declare":
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ func retryRefusal(p inventory.Plan, plans []inventory.Plan) error {
|
|||||||
return fmt.Errorf("nothing in tier %d of %s failed to build or stopped rolling out — it stopped at: %s",
|
return fmt.Errorf("nothing in tier %d of %s failed to build or stopped rolling out — it stopped at: %s",
|
||||||
p.Tier, p.ID, p.Note)
|
p.Tier, p.ID, p.Note)
|
||||||
}
|
}
|
||||||
// **A build that failed its gate is not sent again** (novox/hq ADR 0235): it was put back on its first
|
// **A build that failed its gate is not sent again** (novox/hq ADR 0236): it was put back on its first
|
||||||
// machine, and retrying would judge the build the mesh put back, or send the failed one by hand.
|
// machine, and retrying would judge the build the mesh put back, or send the failed one by hand.
|
||||||
for _, m := range stopped {
|
for _, m := range stopped {
|
||||||
if g := p.Modules[m].Gate; g != nil && g.Verdict == inventory.GateFailed {
|
if g := p.Modules[m].Gate; g != nil && g.Verdict == inventory.GateFailed {
|
||||||
|
|||||||
@@ -466,7 +466,7 @@ func pushCommand(ctx context.Context, args []string) error {
|
|||||||
which, len(asked), strings.Join(asked, ", "))
|
which, len(asked), strings.Join(asked, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// **The bus is replaced only as a planned step** (novox/hq ADR 0235, to-be 45 §8): a machine whose bus
|
// **The bus is replaced only as a planned step** (novox/hq ADR 0236, to-be 45 §8): a machine whose bus
|
||||||
// would move is not sent by a push — named, it is refused; otherwise it is left and said.
|
// would move is not sent by a push — named, it is refused; otherwise it is left and said.
|
||||||
busKept, err := busHeld(ctx, inv, asked)
|
busKept, err := busHeld(ctx, inv, asked)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -625,7 +625,7 @@ func TestAPlanStoppedAtItsFirstMachineIsRetried(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Cleanup(func() { sendRollout = was })
|
t.Cleanup(func() { sendRollout = was })
|
||||||
|
|
||||||
// a records: a person's choice, since the default rolls out (novox/hq ADR 0235).
|
// a records: a person's choice, since the default rolls out (novox/hq ADR 0236).
|
||||||
if err := open.inventory.SetUpgradeOf(ctx, "a", inventory.Upgrade{Why: "test"}); err != nil {
|
if err := open.inventory.SetUpgradeOf(ctx, "a", inventory.Upgrade{Why: "test"}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ func planBuilt(ctx context.Context, open *stores, module, commit, failed string,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if failed != "" && deletedAtSource(failed) {
|
if failed != "" && deletedAtSource(failed) {
|
||||||
// Deleted at its source by the merge, not broken (novox/hq ADR 0235): the plan goes on.
|
// Deleted at its source by the merge, not broken (novox/hq ADR 0236): the plan goes on.
|
||||||
state.State, state.Why = planDeleted, "deleted at its source: "+firstLine(failed)
|
state.State, state.Why = planDeleted, "deleted at its source: "+firstLine(failed)
|
||||||
forgetDeleted(ctx, inv, module, p)
|
forgetDeleted(ctx, inv, module, p)
|
||||||
} else if failed != "" {
|
} else if failed != "" {
|
||||||
@@ -629,7 +629,7 @@ func advanceOnce(ctx context.Context, open *stores, p *inventory.Plan,
|
|||||||
switch {
|
switch {
|
||||||
case step.failed != "":
|
case step.failed != "":
|
||||||
// The first machine refused or failed what it was sent, or never said: the gate failed, and
|
// The first machine refused or failed what it was sent, or never said: the gate failed, and
|
||||||
// the build is put back there (novox/hq ADR 0235); the rest are left as they were.
|
// the build is put back there (novox/hq ADR 0236); the rest are left as they were.
|
||||||
gateFailed(ctx, open, p, m, state, firstRunning(state.First, running), step.failed)
|
gateFailed(ctx, open, p, m, state, firstRunning(state.First, running), step.failed)
|
||||||
p.Note += fmt.Sprintf("; %s left as it was", orNone(strings.Join(step.rest, ", ")))
|
p.Note += fmt.Sprintf("; %s left as it was", orNone(strings.Join(step.rest, ", ")))
|
||||||
fmt.Printf("%s: %s\n", p.ID, p.Note)
|
fmt.Printf("%s: %s\n", p.ID, p.Note)
|
||||||
@@ -638,7 +638,7 @@ func advanceOnce(ctx context.Context, open *stores, p *inventory.Plan,
|
|||||||
pending = append(pending, fmt.Sprintf("%s on %s, sent first at %s", m, step.waiting, state.FirstAt.Local().Format("15:04")))
|
pending = append(pending, fmt.Sprintf("%s on %s, sent first at %s", m, step.waiting, state.FirstAt.Local().Format("15:04")))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// **The gate** (novox/hq ADR 0235, to-be 45 §8): the first machine reported the build applied;
|
// **The gate** (novox/hq ADR 0236, to-be 45 §8): the first machine reported the build applied;
|
||||||
// it is judged by its health before anything else is sent — the rest, or, where it is the only
|
// it is judged by its health before anything else is sent — the rest, or, where it is the only
|
||||||
// machine, the plan's next step.
|
// machine, the plan's next step.
|
||||||
if !policy.Together && state.FirstAt != nil && len(state.First) > 0 {
|
if !policy.Together && state.FirstAt != nil && len(state.First) > 0 {
|
||||||
@@ -684,7 +684,7 @@ func advanceOnce(ctx context.Context, open *stores, p *inventory.Plan,
|
|||||||
return false, fmt.Errorf("sending %s to %s after tier %d: %w", m, strings.Join(step.send, ", "), p.Tier, err)
|
return false, fmt.Errorf("sending %s to %s after tier %d: %w", m, strings.Join(step.send, ", "), p.Tier, err)
|
||||||
}
|
}
|
||||||
if step.first {
|
if step.first {
|
||||||
// What the first machine ran before: what a failed gate puts back (ADR 0235).
|
// What the first machine ran before: what a failed gate puts back (ADR 0236).
|
||||||
if beforeKnown {
|
if beforeKnown {
|
||||||
state.Previous = before[m]
|
state.Previous = before[m]
|
||||||
}
|
}
|
||||||
@@ -1328,7 +1328,7 @@ func firstRunning(first, running []string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// planDeleted is a plan's module that the merge deleted at its source: not built, not sent, and no
|
// planDeleted is a plan's module that the merge deleted at its source: not built, not sent, and no
|
||||||
// failure of the plan (novox/hq ADR 0235).
|
// failure of the plan (novox/hq ADR 0236).
|
||||||
const planDeleted = "deleted"
|
const planDeleted = "deleted"
|
||||||
|
|
||||||
// forgetDeleted forgets a module a plan found deleted at its source, where nothing holds it, and says
|
// forgetDeleted forgets a module a plan found deleted at its source, where nothing holds it, and says
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ func (l nudgingListener) Heard(ctx context.Context, report link.Report) (bool, e
|
|||||||
if report.Ordered() {
|
if report.Ordered() {
|
||||||
link.StaleRefusals.Lifetime(report.Node, report.RefusedOlder, now)
|
link.StaleRefusals.Lifetime(report.Node, report.RefusedOlder, now)
|
||||||
}
|
}
|
||||||
// What the machine's witnesses put back and stand by (novox/hq ADR 0235): read by the gate and its
|
// What the machine's witnesses put back and stand by (novox/hq ADR 0236): read by the gate and its
|
||||||
// probe. Only from an account of the machine — not a word that a declaration was set aside, nor a rekey.
|
// probe. Only from an account of the machine — not a word that a declaration was set aside, nor a rekey.
|
||||||
if report.Superseded == "" && report.Rekey == nil && report.Node != "" {
|
if report.Superseded == "" && report.Rekey == nil && report.Node != "" {
|
||||||
witnessed.heard(report.Node, report.Rollbacks, now)
|
witnessed.heard(report.Node, report.Rollbacks, now)
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ func upgradeCommand(ctx context.Context, args []string) error {
|
|||||||
}
|
}
|
||||||
if decision.Why == "" {
|
if decision.Why == "" {
|
||||||
return fmt.Errorf("holding %s back from every merge is a choice a person reads later: --why <text> "+
|
return fmt.Errorf("holding %s back from every merge is a choice a person reads later: --why <text> "+
|
||||||
"(novox/hq ADR 0235). Nothing was changed", module)
|
"(novox/hq ADR 0236). Nothing was changed", module)
|
||||||
}
|
}
|
||||||
case "default":
|
case "default":
|
||||||
if err := inv.ClearUpgradeOf(ctx, module); err != nil {
|
if err := inv.ClearUpgradeOf(ctx, module); err != nil {
|
||||||
@@ -354,7 +354,7 @@ func (f following) SourceMoved(ctx context.Context, m link.SourceMoved) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
touched := whatTheMergeTouched(from, entries, m)
|
touched := whatTheMergeTouched(from, entries, m)
|
||||||
// **A module the merge deleted is not built** (novox/hq ADR 0235): its manifest is gone, so the build
|
// **A module the merge deleted is not built** (novox/hq ADR 0236): its manifest is gone, so the build
|
||||||
// seat finds nothing saying what it is, and the plan failed on it (`has no module.json at …`) with
|
// seat finds nothing saying what it is, and the plan failed on it (`has no module.json at …`) with
|
||||||
// every other module of its tier left unsent. It is forgotten where nothing holds it, said otherwise.
|
// every other module of its tier left unsent. It is forgotten where nothing holds it, said otherwise.
|
||||||
touched, deleted := splitDeleted(touched, m)
|
touched, deleted := splitDeleted(touched, m)
|
||||||
@@ -521,7 +521,7 @@ func wouldMove(m link.SourceMoved, entries []inventory.Entry,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// splitDeleted parts the modules a merge touched into those it changed and those whose manifest it
|
// splitDeleted parts the modules a merge touched into those it changed and those whose manifest it
|
||||||
// removed — deleted at their source (ADR 0235).
|
// removed — deleted at their source (ADR 0236).
|
||||||
func splitDeleted(touched []inventory.Entry, m link.SourceMoved) (kept, deleted []inventory.Entry) {
|
func splitDeleted(touched []inventory.Entry, m link.SourceMoved) (kept, deleted []inventory.Entry) {
|
||||||
removed := map[string]bool{}
|
removed := map[string]bool{}
|
||||||
for _, p := range m.Removed {
|
for _, p := range m.Removed {
|
||||||
@@ -852,7 +852,7 @@ const moduleManifestFile = "module.json"
|
|||||||
|
|
||||||
// deletedAtSource is whether a build failed because its module's manifest is not at its source any
|
// deletedAtSource is whether a build failed because its module's manifest is not at its source any
|
||||||
// more — the build seat's own words (internal/builder) — which a merge that deleted the module causes
|
// more — the build seat's own words (internal/builder) — which a merge that deleted the module causes
|
||||||
// when its announcer did not say which files went (ADR 0235). Such a module is not a failure of the plan.
|
// when its announcer did not say which files went (ADR 0236). Such a module is not a failure of the plan.
|
||||||
func deletedAtSource(failed string) bool {
|
func deletedAtSource(failed string) bool {
|
||||||
return strings.Contains(failed, "has no "+moduleManifestFile+" at ") &&
|
return strings.Contains(failed, "has no "+moduleManifestFile+" at ") &&
|
||||||
strings.Contains(failed, "so there is nothing saying what it is")
|
strings.Contains(failed, "so there is nothing saying what it is")
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/novox/mesh-controller/internal/lease"
|
"github.com/novox/mesh-controller/internal/lease"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The controller's side of its own rollback witness (novox/hq to-be 45 §8, ADR 0235; the contract is
|
// The controller's side of its own rollback witness (novox/hq to-be 45 §8, ADR 0236; the contract is
|
||||||
// internal/lease/witness.go): what the serving controller says of itself in every write of the lease's
|
// internal/lease/witness.go): what the serving controller says of itself in every write of the lease's
|
||||||
// key, for the node-engine on the control node to judge a new controller build by.
|
// key, for the node-engine on the control node to judge a new controller build by.
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func TestTheFactsTheGrantPermitsAreTheFactsTheMeshStates(t *testing.T) {
|
|||||||
states = append(states, link.KeySecretReplaced)
|
states = append(states, link.KeySecretReplaced)
|
||||||
// And every act a healer takes (novox/hq to-be 45 §7).
|
// And every act a healer takes (novox/hq to-be 45 §7).
|
||||||
states = append(states, link.KeyHealerActed)
|
states = append(states, link.KeyHealerActed)
|
||||||
// And a build put back after its gate failed (novox/hq ADR 0235).
|
// And a build put back after its gate failed (novox/hq ADR 0236).
|
||||||
states = append(states, link.KeyRolledBack)
|
states = append(states, link.KeyRolledBack)
|
||||||
for _, event := range states {
|
for _, event := range states {
|
||||||
if !slices.Contains(broker.ControllerStates, event) {
|
if !slices.Contains(broker.ControllerStates, event) {
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ var ControllerStates = []string{"applied", "refused", "built-before",
|
|||||||
// And every act a healer takes on a condition (novox/hq to-be 45 §7, Phase 3): a repair the mesh
|
// And every act a healer takes on a condition (novox/hq to-be 45 §7, Phase 3): a repair the mesh
|
||||||
// made by itself is said like one a person made, never quietly.
|
// made by itself is said like one a person made, never quietly.
|
||||||
"healer-acted",
|
"healer-acted",
|
||||||
// And a build put back after its gate failed on its first machine (novox/hq ADR 0235, to-be 45 §8).
|
// And a build put back after its gate failed on its first machine (novox/hq ADR 0236, to-be 45 §8).
|
||||||
"rolled-back"}
|
"rolled-back"}
|
||||||
|
|
||||||
// BusAdvisories are what the bus server says about the mesh's own account that the controller
|
// BusAdvisories are what the bus server says about the mesh's own account that the controller
|
||||||
|
|||||||
@@ -453,7 +453,7 @@ type Manifest struct {
|
|||||||
// unassignment retires and what the self-check measures are all derived from it.
|
// unassignment retires and what the self-check measures are all derived from it.
|
||||||
Data *Data `json:"data,omitempty"`
|
Data *Data `json:"data,omitempty"`
|
||||||
|
|
||||||
// Upgrade is how this module's new builds reach its machines (novox/hq ADR 0235): rolled out one
|
// Upgrade is how this module's new builds reach its machines (novox/hq ADR 0236): rolled out one
|
||||||
// machine first and gated when unsaid; `together`, or `record` — wait for a person's push — with
|
// machine first and gated when unsaid; `together`, or `record` — wait for a person's push — with
|
||||||
// why. A person's choice through the `upgrade` verb stands over it; the bus records whatever it says.
|
// why. A person's choice through the `upgrade` verb stands over it; the bus records whatever it says.
|
||||||
Upgrade *UpgradePolicy `json:"upgrade,omitempty"`
|
Upgrade *UpgradePolicy `json:"upgrade,omitempty"`
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ var defaultSeats = append([]Seat{
|
|||||||
"secret-replaced",
|
"secret-replaced",
|
||||||
// Every act a healer takes (novox/hq to-be 45 §7).
|
// Every act a healer takes (novox/hq to-be 45 §7).
|
||||||
"healer-acted",
|
"healer-acted",
|
||||||
// A build put back after its gate failed (novox/hq ADR 0235, to-be 45 §8).
|
// A build put back after its gate failed (novox/hq ADR 0236, to-be 45 §8).
|
||||||
"rolled-back"},
|
"rolled-back"},
|
||||||
Serves: ControllerVerbs},
|
Serves: ControllerVerbs},
|
||||||
// The store's first verbs (novox/hq ADR 0159): the smallest set that makes the store askable,
|
// The store's first verbs (novox/hq ADR 0159): the smallest set that makes the store askable,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// What the mesh does when a module's build moves (novox/hq ADR 0235, extending ADR 0162 §3 and ADR
|
// What the mesh does when a module's build moves (novox/hq ADR 0236, extending ADR 0162 §3 and ADR
|
||||||
// 0218 §2).
|
// 0218 §2).
|
||||||
//
|
//
|
||||||
// **Rolled out by default, one machine first and gated.** With the gate on the first machine and the
|
// **Rolled out by default, one machine first and gated.** With the gate on the first machine and the
|
||||||
@@ -62,7 +62,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// DerivedUpgrade is the policy a module's manifest gives it when no person has chosen one, with where
|
// 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):
|
// 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
|
// - **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);
|
// upgrade is a planned step a person starts (to-be 45 §8);
|
||||||
|
|||||||
@@ -267,8 +267,8 @@ var ControllerVerbs = []Verb{
|
|||||||
"probes": "\"true\": the registry — what each probe asserts, and the condition it raises",
|
"probes": "\"true\": the registry — what each probe asserts, and the condition it raises",
|
||||||
"signals": "\"true\": the signals table, each row with the age of its newest signal",
|
"signals": "\"true\": the signals table, each row with the age of its newest signal",
|
||||||
}, nil, "run", "probes", "signals")},
|
}, nil, "run", "probes", "signals")},
|
||||||
// How a module's new builds reach its machines, and the bus's planned step (novox/hq ADR 0235).
|
// How a module's new builds reach its machines, and the bus's planned step (novox/hq ADR 0236).
|
||||||
{Name: "upgrade", Description: "How each module's new builds reach its machines (novox/hq ADR 0235): rolled " +
|
{Name: "upgrade", Description: "How each module's new builds reach its machines (novox/hq ADR 0236): rolled " +
|
||||||
"out one machine first and judged there at the gate, then the rest — or recorded, waiting for a person's " +
|
"out one machine first and judged there at the gate, then the rest — or recorded, waiting for a person's " +
|
||||||
"push — with where that comes from (a person, the module, the bus, its irreplaceable data, the default) and " +
|
"push — with where that comes from (a person, the module, the bus, its irreplaceable data, the default) and " +
|
||||||
"why. With module, that one; with policy, a person's choice for it — roll-out, record (with why) or default " +
|
"why. With module, that one; with policy, a person's choice for it — roll-out, record (with why) or default " +
|
||||||
@@ -279,7 +279,7 @@ var ControllerVerbs = []Verb{
|
|||||||
"together": "\"true\": with roll-out, every machine at once instead of one machine first",
|
"together": "\"true\": with roll-out, every machine at once instead of one machine first",
|
||||||
"why": "with policy: why — required for record, kept and said with the policy",
|
"why": "with policy: why — required for record, kept and said with the policy",
|
||||||
}, nil, "together")},
|
}, nil, "together")},
|
||||||
{Name: "bus", Description: "The bus as a planned step (novox/hq to-be 45 §8, ADR 0235): what a bus upgrade " +
|
{Name: "bus", Description: "The bus as a planned step (novox/hq to-be 45 §8, ADR 0236): what a bus upgrade " +
|
||||||
"would do — the bus's build on each machine against the one the mesh holds — and how the last step went. " +
|
"would do — the bus's build on each machine against the one the mesh holds — and how the last step went. " +
|
||||||
"With upgrade, start one: a person's act with why, after the streams are snapshotted (snapshot-taken says " +
|
"With upgrade, start one: a person's act with why, after the streams are snapshotted (snapshot-taken says " +
|
||||||
"where, while the mesh takes none itself), saying first whether it can be reverted; bus-maintenance is open " +
|
"where, while the mesh takes none itself), saying first whether it can be reverted; bus-maintenance is open " +
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BusStep is one planned bus upgrade (novox/hq to-be 45 §8, ADR 0235).
|
// BusStep is one planned bus upgrade (novox/hq to-be 45 §8, ADR 0236).
|
||||||
type BusStep struct {
|
type BusStep struct {
|
||||||
ID int64
|
ID int64
|
||||||
Module string
|
Module string
|
||||||
|
|||||||
@@ -1194,10 +1194,10 @@ func (i *Inventory) Catalogued(ctx context.Context) ([]Entry, error) {
|
|||||||
// providedBy is what the source column says for a module the control plane ships.
|
// providedBy is what the source column says for a module the control plane ships.
|
||||||
const providedBy = "the control plane"
|
const providedBy = "the control plane"
|
||||||
|
|
||||||
// Upgrade is what the mesh does when a module's current version moves (ADR 0162 §3, ADR 0235).
|
// Upgrade is what the mesh does when a module's current version moves (ADR 0162 §3, ADR 0236).
|
||||||
type Upgrade struct {
|
type Upgrade struct {
|
||||||
// RollOut is true when the machines running it are sent the new version: one machine first, judged
|
// 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
|
// at the gate, then the rest (ADR 0218, ADR 0236). False means record it and stop — the machines
|
||||||
// running it are behind until a person pushes, which the mesh already reports.
|
// running it are behind until a person pushes, which the mesh already reports.
|
||||||
RollOut bool
|
RollOut bool
|
||||||
// Together is true when every machine running it is sent the new version at once. Only meaningful
|
// Together is true when every machine running it is sent the new version at once. Only meaningful
|
||||||
@@ -1223,7 +1223,7 @@ func (u Upgrade) Policy() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// upgradeFrom is a module's policy from what the store holds of it: a person's choice, over the module's
|
// 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).
|
// own word, over the default — except that the bus is never rolled out, whoever says so (ADR 0236).
|
||||||
func upgradeFrom(chosen *string, together bool, why, by string, manifest []byte) Upgrade {
|
func upgradeFrom(chosen *string, together bool, why, by string, manifest []byte) Upgrade {
|
||||||
var m catalogue.Manifest
|
var m catalogue.Manifest
|
||||||
// Leniently: a policy is read from what was registered, and a manifest registered before a field it
|
// Leniently: a policy is read from what was registered, and a manifest registered before a field it
|
||||||
@@ -1296,7 +1296,7 @@ func (i *Inventory) Upgrades(ctx context.Context) (map[string]Upgrade, error) {
|
|||||||
return out, rows.Err()
|
return out, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrBusIsPlanned is a person asking the bus to be rolled out: its upgrade is a planned step (ADR 0235).
|
// ErrBusIsPlanned is a person asking the bus to be rolled out: its upgrade is a planned step (ADR 0236).
|
||||||
var ErrBusIsPlanned = errors.New("the bus is never rolled out: its upgrade is a planned step a person starts " +
|
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")
|
"with `bus upgrade`, which snapshots its streams first and checks them after")
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/novox/mesh-controller/internal/catalogue"
|
"github.com/novox/mesh-controller/internal/catalogue"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The gate's verdicts (novox/hq ADR 0235, to-be 45 §8): what a build did on its first machine, and,
|
// The gate's verdicts (novox/hq ADR 0236, to-be 45 §8): what a build did on its first machine, and,
|
||||||
// for one that failed there, how it was put back. One row per build, written by the plan that rolled it
|
// for one that failed there, how it was put back. One row per build, written by the plan that rolled it
|
||||||
// out, under the lease.
|
// out, under the lease.
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
-- A module's build rolls out one machine first, judged at a gate, and rolls back there when the gate
|
-- A module's build rolls out one machine first, judged at a gate, and rolls back there when the gate
|
||||||
-- fails (novox/hq ADR 0235, to-be 45 §8, Phase 4).
|
-- fails (novox/hq ADR 0236, to-be 45 §8, Phase 4).
|
||||||
--
|
--
|
||||||
-- 1. The upgrade policy becomes a person's choice over the module's own word. Until now every module
|
-- 1. The upgrade policy becomes a person's choice over the module's own word. Until now every module
|
||||||
-- held a policy here, 'record' unless a person had said 'roll-out', and nothing distinguished a
|
-- held a policy here, 'record' unless a person had said 'roll-out', and nothing distinguished a
|
||||||
-- 'record' somebody chose from the default it always was. From here a null policy is no choice: the
|
-- 'record' somebody chose from the default it always was. From here a null policy is no choice: the
|
||||||
-- module's manifest decides (its `upgrade`, its data, whether it is the bus), and its default is to
|
-- module's manifest decides (its `upgrade`, its data, whether it is the bus), and its default is to
|
||||||
-- roll out. A 'roll-out' a person chose is kept as their choice. A 'record' is the old default and
|
-- roll out. A 'roll-out' a person chose is kept as their choice. A 'record' is the old default and
|
||||||
-- becomes no choice — ADR 0235 decides it, and lists every module's resulting policy; a person who
|
-- becomes no choice — ADR 0236 decides it, and lists every module's resulting policy; a person who
|
||||||
-- wants one held again says so with `upgrade <module> record --why`, which is kept with its why.
|
-- wants one held again says so with `upgrade <module> record --why`, which is kept with its why.
|
||||||
alter table module alter column upgrade drop not null;
|
alter table module alter column upgrade drop not null;
|
||||||
alter table module alter column upgrade drop default;
|
alter table module alter column upgrade drop default;
|
||||||
|
|||||||
@@ -69,10 +69,10 @@ type PlanModule struct {
|
|||||||
Build string `json:"build,omitempty"`
|
Build string `json:"build,omitempty"`
|
||||||
// Previous is the build the first machine ran of this module before the plan sent it the new one —
|
// Previous is the build the first machine ran of this module before the plan sent it the new one —
|
||||||
// the commit its last send carried (ADR 0221) — kept at the first send: what a rollback puts back
|
// the commit its last send carried (ADR 0221) — kept at the first send: what a rollback puts back
|
||||||
// (novox/hq ADR 0235). Empty when the machine had never been sent the module, or what it was sent
|
// (novox/hq ADR 0236). Empty when the machine had never been sent the module, or what it was sent
|
||||||
// is not known.
|
// is not known.
|
||||||
Previous string `json:"previous,omitempty"`
|
Previous string `json:"previous,omitempty"`
|
||||||
// Gate is the new build's judging on its first machine (novox/hq ADR 0235, to-be 45 §8), kept so a
|
// Gate is the new build's judging on its first machine (novox/hq ADR 0236, to-be 45 §8), kept so a
|
||||||
// controller replaced mid-judging resumes it, and read back through `plans` as the rollout's record.
|
// controller replaced mid-judging resumes it, and read back through `plans` as the rollout's record.
|
||||||
Gate *PlanGate `json:"gate,omitempty"`
|
Gate *PlanGate `json:"gate,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ func TestTheCurrentBuildsAreTheCatalogues(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
// Rolled out by default, one machine first and gated (novox/hq ADR 0235).
|
// Rolled out by default, one machine first and gated (novox/hq ADR 0236).
|
||||||
if got := current["resolver"]; got != (CurrentBuild{Commit: "c1", RollOut: true}) {
|
if got := current["resolver"]; got != (CurrentBuild{Commit: "c1", RollOut: true}) {
|
||||||
t.Errorf("resolver is at %+v", got)
|
t.Errorf("resolver is at %+v", got)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ const (
|
|||||||
// are the hand-act log's.
|
// are the hand-act log's.
|
||||||
KeyHealerActed = "healer-acted"
|
KeyHealerActed = "healer-acted"
|
||||||
// KeyRolledBack: a build failed its gate on its first machine and was put back there, or could not
|
// KeyRolledBack: a build failed its gate on its first machine and was put back there, or could not
|
||||||
// be (novox/hq ADR 0235, to-be 45 §8); or a witness on a machine put a core component back.
|
// be (novox/hq ADR 0236, to-be 45 §8); or a witness on a machine put a core component back.
|
||||||
KeyRolledBack = "rolled-back"
|
KeyRolledBack = "rolled-back"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ type SourceMoved struct {
|
|||||||
PathsTruncated bool `json:"paths_truncated,omitempty"`
|
PathsTruncated bool `json:"paths_truncated,omitempty"`
|
||||||
|
|
||||||
// Removed are the files among Paths the merge deleted. A module whose manifest is among them was
|
// Removed are the files among Paths the merge deleted. A module whose manifest is among them was
|
||||||
// deleted at its source: it is forgotten, or said, and never built (novox/hq ADR 0235). Empty from an
|
// deleted at its source: it is forgotten, or said, and never built (novox/hq ADR 0236). Empty from an
|
||||||
// announcer that does not say which files went, and then a build that finds no manifest says it.
|
// announcer that does not say which files went, and then a build that finds no manifest says it.
|
||||||
Removed []string `json:"removed,omitempty"`
|
Removed []string `json:"removed,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user