Refuse an unmet seat dependency the catalogue could meet (hq ADR 0207 §4)
status reported no unmet dependency on any node once systemd, pacman and docker were assigned to all four (to-be 42), which is the condition ADR 0207 set for the switch. A dependency no catalogue module could meet stays a report before and after the switch, as assign already said it: there is no remedy to name.
This commit is contained in:
@@ -105,6 +105,8 @@ func TestUnassigningTheLastHolderUnderItsDependentsIsRefused(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStatusReportsAnUnheldDependencyWithoutRefusingTheMachine(t *testing.T) {
|
func TestStatusReportsAnUnheldDependencyWithoutRefusingTheMachine(t *testing.T) {
|
||||||
|
// The mesh as it ran before the switch (novox/hq ADR 0207 §4).
|
||||||
|
defer catalogue.EnforcingSeatDependencies(false)()
|
||||||
open := aMesh(t)
|
open := aMesh(t)
|
||||||
ctx := t.Context()
|
ctx := t.Context()
|
||||||
register(t, open, serviceManagerHolder())
|
register(t, open, serviceManagerHolder())
|
||||||
|
|||||||
@@ -675,11 +675,16 @@ func Resolve(catalogue map[string]Manifest, assigned []string, node Node, world
|
|||||||
// Reported until the switch; refused after it, though never in the first pass, whose refusals
|
// Reported until the switch; refused after it, though never in the first pass, whose refusals
|
||||||
// make a machine vanish from the network rather than report anything.
|
// make a machine vanish from the network rather than report anything.
|
||||||
resolution.Unheld = UnheldDependencies(catalogue, node.Name, resolution.Modules, nil)
|
resolution.Unheld = UnheldDependencies(catalogue, node.Name, resolution.Modules, nil)
|
||||||
|
// Only a dependency some module in the catalogue could meet is refused: one with no possible
|
||||||
|
// holder has no remedy to name, and stays a report in `status` (novox/hq ADR 0207 §4, read with
|
||||||
|
// the assign rule above it).
|
||||||
if enforceSeatDependencies && !world.Unchecked {
|
if enforceSeatDependencies && !world.Unchecked {
|
||||||
for _, u := range resolution.Unheld {
|
for _, u := range resolution.Unheld {
|
||||||
|
if len(u.Holders) > 0 {
|
||||||
problems = append(problems, u.String())
|
problems = append(problems, u.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(problems) > 0 {
|
if len(problems) > 0 {
|
||||||
sort.Strings(problems)
|
sort.Strings(problems)
|
||||||
|
|||||||
@@ -39,8 +39,19 @@ var appliedThrough = map[string]string{
|
|||||||
// which is when the three holders are assigned to every node: switching it before then would stop
|
// which is when the three holders are assigned to every node: switching it before then would stop
|
||||||
// every machine lacking one from being sent anything at all.
|
// every machine lacking one from being sent anything at all.
|
||||||
//
|
//
|
||||||
|
// Switched on 2026-10-04, when `status` first reported no unmet dependency on any node: systemd,
|
||||||
|
// pacman and docker were assigned to all four machines that afternoon (novox/hq to-be 42).
|
||||||
|
//
|
||||||
// A variable rather than a constant only so a test can hold both behaviours; nothing else sets it.
|
// A variable rather than a constant only so a test can hold both behaviours; nothing else sets it.
|
||||||
var enforceSeatDependencies = false
|
var enforceSeatDependencies = true
|
||||||
|
|
||||||
|
// EnforcingSeatDependencies sets the switch and returns what puts it back. For tests in other
|
||||||
|
// packages that hold the behaviour from before the switch; nothing else calls it.
|
||||||
|
func EnforcingSeatDependencies(on bool) (restore func()) {
|
||||||
|
was := enforceSeatDependencies
|
||||||
|
enforceSeatDependencies = on
|
||||||
|
return func() { enforceSeatDependencies = was }
|
||||||
|
}
|
||||||
|
|
||||||
// foundationModules are the pieces genesis lays before any module exists (novox/hq ADR 0207 §5):
|
// foundationModules are the pieces genesis lays before any module exists (novox/hq ADR 0207 §5):
|
||||||
// the host and the private network. Registered as modules so they can be assigned, but what they
|
// the host and the private network. Registered as modules so they can be assigned, but what they
|
||||||
@@ -207,8 +218,8 @@ func manifestsOf(catalogue map[string]Manifest, names []string) []Manifest {
|
|||||||
// **A dependency nothing in the catalogue can meet is said, not refused.** A refusal names the
|
// **A dependency nothing in the catalogue can meet is said, not refused.** A refusal names the
|
||||||
// module that would meet it; with none registered there is no remedy to name, and refusing would
|
// module that would meet it; with none registered there is no remedy to name, and refusing would
|
||||||
// stop every assignment of that kind until a module that does not exist yet is written. The answer
|
// stop every assignment of that kind until a module that does not exist yet is written. The answer
|
||||||
// still says it, and `status` reports it, until the switch (enforceSeatDependencies) makes the mesh
|
// still says it, and `status` reports it — before the switch and after it alike: there is never a
|
||||||
// refuse what it cannot meet. The first return is those lines.
|
// remedy to name for it. The first return is those lines.
|
||||||
func AssignRefusal(catalogue map[string]Manifest, node string, assigned, adding []string) ([]string, error) {
|
func AssignRefusal(catalogue map[string]Manifest, node string, assigned, adding []string) ([]string, error) {
|
||||||
set := manifestsOf(catalogue, append(append([]string(nil), assigned...), adding...))
|
set := manifestsOf(catalogue, append(append([]string(nil), assigned...), adding...))
|
||||||
judged := map[string]bool{}
|
judged := map[string]bool{}
|
||||||
@@ -217,7 +228,7 @@ func AssignRefusal(catalogue map[string]Manifest, node string, assigned, adding
|
|||||||
}
|
}
|
||||||
var refused, said []string
|
var refused, said []string
|
||||||
for _, u := range UnheldDependencies(catalogue, node, set, judged) {
|
for _, u := range UnheldDependencies(catalogue, node, set, judged) {
|
||||||
if len(u.Holders) == 0 && !enforceSeatDependencies {
|
if len(u.Holders) == 0 {
|
||||||
said = append(said, u.String())
|
said = append(said, u.String())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,24 +120,25 @@ func TestAnAssignmentMissingAHolderIsRefusedNamingTheSeatAndItsPossibleHolders(t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestADependencyNoCatalogueModuleCanMeetIsSaidNotRefusedUntilTheSwitch(t *testing.T) {
|
func TestADependencyNoCatalogueModuleCanMeetIsSaidNeverRefused(t *testing.T) {
|
||||||
// No runtime module in the catalogue: refusing would stop every container's assignment until one
|
// No runtime module in the catalogue: refusing would stop every container's assignment until one
|
||||||
// is written, with no remedy to name.
|
// is written, with no remedy to name — so it is said, with the switch on as with it off.
|
||||||
web := withResources(mod("web", nil, nil, nil), res("container", "web"))
|
web := withResources(mod("web", nil, nil, nil), res("container", "web"))
|
||||||
cat := shelf(web)
|
cat := shelf(web)
|
||||||
|
for _, on := range []bool{false, true} {
|
||||||
|
enforceSeatDependencies = on
|
||||||
said, err := AssignRefusal(cat, "workstation", nil, []string{"web"})
|
said, err := AssignRefusal(cat, "workstation", nil, []string{"web"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("a dependency nothing could meet was refused: %v", err)
|
t.Fatalf("switch %v: a dependency nothing could meet was refused: %v", on, err)
|
||||||
}
|
}
|
||||||
if len(said) != 1 || !strings.Contains(said[0], "no module in the catalogue claims it yet") {
|
if len(said) != 1 || !strings.Contains(said[0], "no module in the catalogue claims it yet") {
|
||||||
t.Errorf("the assignment does not say what it depends on: %v", said)
|
t.Errorf("switch %v: the assignment does not say what it depends on: %v", on, said)
|
||||||
|
}
|
||||||
|
if _, err := Resolve(cat, []string{"web"}, workstation(), World{}); err != nil {
|
||||||
|
t.Errorf("switch %v: a dependency nothing could meet refused the node: %v", on, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enforceSeatDependencies = true
|
enforceSeatDependencies = true
|
||||||
defer func() { enforceSeatDependencies = false }()
|
|
||||||
if _, err := AssignRefusal(cat, "workstation", nil, []string{"web"}); err == nil {
|
|
||||||
t.Error("with the switch on, a dependency nothing could meet was not refused")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTheHoldersMutualDependenceResolvesWhenAssignedTogether(t *testing.T) {
|
func TestTheHoldersMutualDependenceResolvesWhenAssignedTogether(t *testing.T) {
|
||||||
@@ -165,6 +166,9 @@ func TestTheHoldersMutualDependenceResolvesWhenAssignedTogether(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStatusIsToldOfAnUnmetDependencyAndTheNodeStillResolves(t *testing.T) {
|
func TestStatusIsToldOfAnUnmetDependencyAndTheNodeStillResolves(t *testing.T) {
|
||||||
|
// Before the switch (the state the mesh ran in until every node held the three seats).
|
||||||
|
enforceSeatDependencies = false
|
||||||
|
defer func() { enforceSeatDependencies = true }()
|
||||||
web := withResources(mod("web", nil, nil, nil), res("container", "web"))
|
web := withResources(mod("web", nil, nil, nil), res("container", "web"))
|
||||||
cat := shelf(append(coreThree(), web)...)
|
cat := shelf(append(coreThree(), web)...)
|
||||||
got, err := Resolve(cat, []string{"web"}, workstation(), World{})
|
got, err := Resolve(cat, []string{"web"}, workstation(), World{})
|
||||||
@@ -179,7 +183,7 @@ func TestStatusIsToldOfAnUnmetDependencyAndTheNodeStillResolves(t *testing.T) {
|
|||||||
|
|
||||||
func TestWithTheSwitchFlippedAnUnmetDependencyRefusesTheNode(t *testing.T) {
|
func TestWithTheSwitchFlippedAnUnmetDependencyRefusesTheNode(t *testing.T) {
|
||||||
enforceSeatDependencies = true
|
enforceSeatDependencies = true
|
||||||
defer func() { enforceSeatDependencies = false }()
|
defer func() { enforceSeatDependencies = true }()
|
||||||
web := withResources(mod("web", nil, nil, nil), res("container", "web"))
|
web := withResources(mod("web", nil, nil, nil), res("container", "web"))
|
||||||
cat := shelf(append(coreThree(), web)...)
|
cat := shelf(append(coreThree(), web)...)
|
||||||
_, err := Resolve(cat, []string{"web"}, workstation(), World{})
|
_, err := Resolve(cat, []string{"web"}, workstation(), World{})
|
||||||
|
|||||||
Reference in New Issue
Block a user