Let the genesis test accept a degradable grant missing from the installer's list (issue 835)
mesh/merge-gate head 5cf8938f55b3 merged into trunk 0ab61d1be8c5 — pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus…
mesh/repo-check head 5cf8938f55b3 merged into trunk 0ab61d1be8c5 — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group feat/mail-seats delivered: every member is delivered
mesh/merge-gate head 5cf8938f55b3 merged into trunk 0ab61d1be8c5 — pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus…
mesh/repo-check head 5cf8938f55b3 merged into trunk 0ab61d1be8c5 — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group feat/mail-seats delivered: every member is delivered
Forge check of mesh-controller#345: the merge check clones mesh-host at the commit the machines run, not its main, so a change that only edits mesh-host's examples (the operator-channel.tell grant) can never be seen there until mesh-host's own module next builds. operator-channel.tell is the one grant with a fallback for a refusal (the secret-given condition, ADR 0380 §9), so mark it degradable and let TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose pass without it while every other grant stays required.
This commit is contained in:
@@ -35,7 +35,7 @@ func TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose(t *testing.T)
|
|||||||
carriedPub := subjectsIn(accounts, "publish")
|
carriedPub := subjectsIn(accounts, "publish")
|
||||||
carriedSub := subjectsIn(accounts, "subscribe")
|
carriedSub := subjectsIn(accounts, "subscribe")
|
||||||
|
|
||||||
if diff := missing(want.Publish, carriedPub); len(diff) > 0 {
|
if diff := requiredPublishGap(t, want.Publish, carriedPub); len(diff) > 0 {
|
||||||
t.Errorf("the installer's user list does not let the controller publish %v — it would come up "+
|
t.Errorf("the installer's user list does not let the controller publish %v — it would come up "+
|
||||||
"and be refused on the first thing it tried", diff)
|
"and be refused on the first thing it tried", diff)
|
||||||
}
|
}
|
||||||
@@ -62,6 +62,59 @@ func TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose(t *testing.T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// requiredPublishGap is what in want is missing from carried, leaving out a grant DegradableGrants names
|
||||||
|
// and saying why in the test log instead (novox/hq issue 835, the forge check of mesh-controller#345): the
|
||||||
|
// merge check reads mesh-host at the commit the machines run, which a change to mesh-host's examples alone
|
||||||
|
// never moves, so the carried list can lag the controller's derived one without the controller refusing to
|
||||||
|
// come up.
|
||||||
|
func requiredPublishGap(t *testing.T, want, carried []string) []string {
|
||||||
|
t.Helper()
|
||||||
|
diff := missing(want, carried)
|
||||||
|
var required []string
|
||||||
|
for _, subject := range diff {
|
||||||
|
if reason, ok := degradableReason(subject); ok {
|
||||||
|
t.Logf("the installer's user list does not let the controller publish %s, but that falls back "+
|
||||||
|
"rather than refuses: %s", subject, reason)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
required = append(required, subject)
|
||||||
|
}
|
||||||
|
return required
|
||||||
|
}
|
||||||
|
|
||||||
|
// degradableReason says whether a subject is one of DegradableGrants, and why.
|
||||||
|
func degradableReason(subject string) (string, bool) {
|
||||||
|
for _, g := range DegradableGrants {
|
||||||
|
if g.Subject == subject {
|
||||||
|
return g.Reason, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestADegradableGrantMissingFromTheInstallersListPasses is the forge check's own case (mesh-controller#345,
|
||||||
|
// novox/hq issue 835): an installer list that lacks operator-channel.tell alone, because mesh-host has not
|
||||||
|
// yet built with the grant, still passes — the controller falls back to the secret-given condition rather
|
||||||
|
// than coming up refused.
|
||||||
|
func TestADegradableGrantMissingFromTheInstallersListPasses(t *testing.T) {
|
||||||
|
want := []string{"mesh.seat.operator-channel.tool.tell", "mesh.seat.node-service-manager.tool.restart.*"}
|
||||||
|
carried := []string{"mesh.seat.node-service-manager.tool.restart.*"}
|
||||||
|
if diff := requiredPublishGap(t, want, carried); len(diff) > 0 {
|
||||||
|
t.Errorf("a degradable grant missing from the installer's list must not fail the genesis test, got %v", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestANonDegradableGrantMissingFromTheInstallersListStillFails proves the exemption is named, not general:
|
||||||
|
// every other grant the controller derives stays required.
|
||||||
|
func TestANonDegradableGrantMissingFromTheInstallersListStillFails(t *testing.T) {
|
||||||
|
want := []string{"mesh.seat.operator-channel.tool.tell", "mesh.seat.node-service-manager.tool.restart.*"}
|
||||||
|
carried := []string{"mesh.seat.operator-channel.tool.tell"}
|
||||||
|
diff := requiredPublishGap(t, want, carried)
|
||||||
|
if len(diff) != 1 || diff[0] != "mesh.seat.node-service-manager.tool.restart.*" {
|
||||||
|
t.Errorf("a missing grant that is not degradable must still fail the genesis test, got %v", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// theCarriedAccounts is the accounts file the installer's template writes at genesis.
|
// theCarriedAccounts is the accounts file the installer's template writes at genesis.
|
||||||
func theCarriedAccounts(t *testing.T) string {
|
func theCarriedAccounts(t *testing.T) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|||||||
@@ -284,6 +284,29 @@ var VerbsTheControllerActsOnAWarrant = []SeatVerb{{Seat: "node-service-manager",
|
|||||||
// where and when and by whom, asking nothing. A mesh seat's verb is flat: no machine in the subject.
|
// where and when and by whom, asking nothing. A mesh seat's verb is flat: no machine in the subject.
|
||||||
var VerbsTheControllerAsksTheOperatorChannel = []SeatVerb{{Seat: "operator-channel", Verb: "tell"}}
|
var VerbsTheControllerAsksTheOperatorChannel = []SeatVerb{{Seat: "operator-channel", Verb: "tell"}}
|
||||||
|
|
||||||
|
// DegradableGrant is a publish grant whose absence from the installer's carried first-user list is not a
|
||||||
|
// bug in the template: a fallback covers the refusal.
|
||||||
|
type DegradableGrant struct {
|
||||||
|
Subject string
|
||||||
|
Reason string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DegradableGrants are the only publish grants TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose
|
||||||
|
// may find missing from the installer's carried list without failing (novox/hq issue 835, the forge check
|
||||||
|
// of mesh-controller#345): a change that only edits mesh-host's examples can never satisfy a check that
|
||||||
|
// reads the mesh-host commit the machines actually run, so the carried list lags behind the controller's
|
||||||
|
// derived one until mesh-host's own module next builds. operator-channel.tell is the one grant the
|
||||||
|
// controller already has a fallback for: refused (no holder, or the bus refuses it), the controller raises
|
||||||
|
// the secret-given condition exactly as before (ADR 0380 §9), so an older carried list still comes up and
|
||||||
|
// works. Every other grant stays required — missing one of those is a controller that comes up and is
|
||||||
|
// refused on the first thing it tries, with no fallback that covers it.
|
||||||
|
var DegradableGrants = []DegradableGrant{
|
||||||
|
{
|
||||||
|
Subject: "mesh.seat." + VerbsTheControllerAsksTheOperatorChannel[0].Seat + ".tool." + VerbsTheControllerAsksTheOperatorChannel[0].Verb,
|
||||||
|
Reason: "refused: the controller raises the secret-given condition as before (ADR 0380 §9)",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// perMachineEvents are a node-scoped seat's events about the holder itself, whose last token is the
|
// perMachineEvents are a node-scoped seat's events about the holder itself, whose last token is the
|
||||||
// holder's machine (novox/hq ADR 0219): `paused.<node>`, the build agent saying whether it takes work.
|
// holder's machine (novox/hq ADR 0219): `paused.<node>`, the build agent saying whether it takes work.
|
||||||
var perMachineEvents = map[string]bool{"paused.*": true}
|
var perMachineEvents = map[string]bool{"paused.*": true}
|
||||||
|
|||||||
Reference in New Issue
Block a user