Resolve: one un-hostable assignment no longer refuses the whole node
A module a person assigns to a machine that cannot host it — its declared capability has no detector there, as fail2ban does on a host with no firewall — made Resolve refuse the entire node, so a whole-node push refused to send the healthy modules beside it too. One module on the wrong machine took down every other module on that node. Assign already keeps such an assignment on purpose (it is what a person meant, and acts.go says so), so the fix is on the resolve/push side: a directly-assigned module the machine cannot host is left out of the closure and reported as un-applied on the Resolution, rather than refusing the set. The healthy modules still resolve, declare, and converge. A module that is *required* by something running here and cannot be hosted still refuses — that set is genuinely incoherent — so the distinction is who wanted it. assign, plan and push now name the un-applied module and the missing capability, via a shared WrongMachine message, so it is neither silently dropped nor fatal. Reconciled two tests that encoded the old whole-node refusal for directly-assigned un-hostable modules; added coverage for the healthy-modules-still-converge case and the required-un-hostable-still-refuses distinction. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -3,6 +3,8 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/novox/mesh-control/internal/catalogue"
|
||||
)
|
||||
|
||||
// The things the mesh can be asked to do, separated from how it was asked.
|
||||
@@ -25,10 +27,22 @@ func assign(ctx context.Context, open *stores, node, module string) (string, err
|
||||
return "", err
|
||||
}
|
||||
said := fmt.Sprintf("%s is assigned %s", node, module)
|
||||
if _, _, err := planFor(ctx, open, node); err != nil {
|
||||
plan, _, err := planFor(ctx, open, node)
|
||||
if err != nil {
|
||||
// Kept, and still refused. Both halves are the answer.
|
||||
return said, err
|
||||
}
|
||||
// Kept, and cannot be hosted here. Said at once rather than discovered at push: a module whose
|
||||
// capability the machine lacks is on the wrong machine, and the assignment records what a person
|
||||
// meant while this line says it will not run until it moves. The rest of the node still pushes.
|
||||
for _, u := range plan.Unhostable {
|
||||
if u.Module != module {
|
||||
continue
|
||||
}
|
||||
for _, c := range u.Missing {
|
||||
said += "\n but " + catalogue.WrongMachine(u.Module, c, node)
|
||||
}
|
||||
}
|
||||
return said + fmt.Sprintf("\n run `push %s` to send it", node), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -611,6 +611,13 @@ func planCommand(ctx context.Context, args []string) error {
|
||||
for _, m := range plan.Modules {
|
||||
fmt.Printf(" %-20s %s\n", m.Module, plan.Because[m.Module])
|
||||
}
|
||||
// What was assigned here and cannot run here. Said with the rest rather than as a refusal: it is
|
||||
// one module on the wrong machine, the others still run, and the remedy is to move this one.
|
||||
for _, u := range plan.Unhostable {
|
||||
for _, c := range u.Missing {
|
||||
fmt.Printf(" %-20s not applied — %s\n", u.Module, catalogue.WrongMachine(u.Module, c, args[0]))
|
||||
}
|
||||
}
|
||||
for _, c := range plan.Claims {
|
||||
fmt.Printf(" holds %s, one per %s\n", c.Claim, c.Scope)
|
||||
}
|
||||
|
||||
@@ -13,10 +13,24 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-control/internal/broker"
|
||||
"github.com/novox/mesh-control/internal/catalogue"
|
||||
"github.com/novox/mesh-control/internal/inventory"
|
||||
"github.com/novox/mesh-control/internal/link"
|
||||
)
|
||||
|
||||
// reportUnhostable says which of a node's assigned modules the machine cannot run, once per push.
|
||||
//
|
||||
// A module whose declared capability has no detector on the machine is on the wrong machine. It is
|
||||
// kept out of what the node is sent — the healthy modules beside it still converge — and named here
|
||||
// so it is neither silently dropped nor a reason the whole node fails to push.
|
||||
func reportUnhostable(node string, plan catalogue.Resolution) {
|
||||
for _, u := range plan.Unhostable {
|
||||
for _, c := range u.Missing {
|
||||
fmt.Printf("%s not applied — %s\n", node, catalogue.WrongMachine(u.Module, c, node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sending it, and holding the link that carries it.
|
||||
//
|
||||
// Split out of main.go, which had reached 2,769 lines because appending was always the
|
||||
@@ -262,6 +276,10 @@ func pushCommand(ctx context.Context, args []string) error {
|
||||
refusals = append(refusals, fmt.Sprintf("%s:\n%v", n.Name, err))
|
||||
continue
|
||||
}
|
||||
// A module assigned here that this machine cannot host is said and left out, not fatal: the
|
||||
// healthy modules beside it are still resolved and sent. Reported so it is not silently
|
||||
// dropped — the remedy is to move it, and until then the rest of the node converges.
|
||||
reportUnhostable(n.Name, plan)
|
||||
// The private network is in here with everything else. It used to be composed separately
|
||||
// and prepended, which meant every machine with an address was on it and no machine could
|
||||
// be kept off. It is a module now, so it arrives the way a module does.
|
||||
@@ -335,6 +353,7 @@ func sendTo(ctx context.Context, open *stores, names []string) error {
|
||||
refusals = append(refusals, fmt.Sprintf("%s:\n%v", name, err))
|
||||
continue
|
||||
}
|
||||
reportUnhostable(name, plan)
|
||||
resources, err := declarationWith(ctx, open, name, plan, settings, gens, Allocating)
|
||||
if err != nil {
|
||||
refusals = append(refusals, fmt.Sprintf("%s:\n%v", name, err))
|
||||
|
||||
Reference in New Issue
Block a user