overlay: placing a node with nothing said no longer unplaces it
The sibling of node public-domain, and the worse one: a placement is three facts declared together, so an invocation that said none of them took all three away — the endpoint every other machine dials, the site, and the hub. A mesh whose hub was placed that way has no paths left, at the moment somebody was trying to look at it. --nothing keeps the real case (a machine that roams and opens every path itself) sayable, by name. Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
This commit is contained in:
@@ -59,7 +59,8 @@ func overlayCommand(ctx context.Context, args []string) error {
|
||||
|
||||
func overlayPlace(ctx context.Context, inv *inventory.Inventory, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.New("overlay place <node> [--endpoint host:port] [--site name] [--hub]")
|
||||
return errors.New(
|
||||
"overlay place <node> [--endpoint host:port] [--site name] [--hub], or --nothing")
|
||||
}
|
||||
node := args[0]
|
||||
|
||||
@@ -67,10 +68,31 @@ func overlayPlace(ctx context.Context, inv *inventory.Inventory, args []string)
|
||||
endpoint := set.String("endpoint", "", "where this node can be dialled, or empty for nowhere")
|
||||
site := set.String("site", "", "where this machine physically is, or empty if it roams")
|
||||
hub := set.Bool("hub", false, "this node is the hub every other routes through")
|
||||
nothing := set.Bool("nothing", false,
|
||||
"place it with nothing set: not dialable, no site, not the hub")
|
||||
if err := set.Parse(args[1:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// **All three are declared together, so saying nothing took all three away.** The sibling of
|
||||
// `node public-domain`: `overlay place anchor` reads like it places the node it names, and it
|
||||
// silently unset the endpoint every other machine dials, the site it is in, and the hub if it
|
||||
// was the hub — every path through it going with them, at the moment somebody was trying to
|
||||
// look at it.
|
||||
//
|
||||
// A placement with nothing set is a real thing to want — a machine that roams and opens every
|
||||
// path itself is exactly that — so it keeps a way to say so, by name.
|
||||
if set.NFlag() == 0 {
|
||||
return fmt.Errorf("overlay place %s was given nothing to place it with, and all three are "+
|
||||
"declared together — it would take away the endpoint other machines dial %s at, its "+
|
||||
"site, and the hub if it is the hub. Say --endpoint/--site/--hub, or --nothing if that "+
|
||||
"is what you meant", node, node)
|
||||
}
|
||||
if *nothing && (*endpoint != "" || *site != "" || *hub) {
|
||||
return fmt.Errorf("give %s a placement or --nothing, not both: they say opposite things "+
|
||||
"and the mesh will not choose between them", node)
|
||||
}
|
||||
|
||||
// Declared, all three. The address is evidence of reachability and is not the fact, and hub
|
||||
// election by address prefix fails silently (novox/hq ADR 0007).
|
||||
if err := inv.SetPlace(ctx, node, *endpoint, *site, *hub, ""); err != nil {
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/novox/mesh-control/internal/inventory"
|
||||
)
|
||||
|
||||
// placementOf is what the mesh holds about where one node is.
|
||||
func placementOf(t *testing.T, ctx context.Context, inv *inventory.Inventory, name string) inventory.Overlay {
|
||||
t.Helper()
|
||||
placed, err := inv.Overlays(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, one := range placed {
|
||||
if one.Name == name {
|
||||
return one
|
||||
}
|
||||
}
|
||||
t.Fatalf("%s is not placed at all", name)
|
||||
return inventory.Overlay{}
|
||||
}
|
||||
|
||||
// The sibling of `node public-domain`, and the worse one: a placement is three facts declared
|
||||
// together, so an invocation that said none of them took all three away — the endpoint every other
|
||||
// machine dials, the site, and the hub. A mesh whose hub was placed that way has no paths left.
|
||||
func TestPlacingANodeWithNothingSaidDoesNotUnplaceIt(t *testing.T) {
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
if err := overlayPlace(ctx, open.inventory,
|
||||
[]string{"anchor", "--endpoint", "198.51.100.10:51820", "--site", "hosting", "--hub"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := overlayPlace(ctx, open.inventory, []string{"anchor"})
|
||||
if err == nil {
|
||||
t.Fatal("saying nothing unplaced the node instead of being refused")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "--nothing") {
|
||||
t.Errorf("the refusal does not say how to mean it: %v", err)
|
||||
}
|
||||
|
||||
held := placementOf(t, ctx, open.inventory, "anchor")
|
||||
if held.Endpoint != "198.51.100.10:51820" || held.Site != "hosting" || !held.Hub {
|
||||
t.Fatalf("the placement was taken away by an invocation that was refused: %+v", held)
|
||||
}
|
||||
}
|
||||
|
||||
// Placing a machine with nothing set is a real thing to want — one that roams and opens every path
|
||||
// itself is exactly that — so it keeps a way to be said, by name.
|
||||
func TestPlacingANodeWithNothingIsAskedForByName(t *testing.T) {
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
if err := overlayPlace(ctx, open.inventory,
|
||||
[]string{"anchor", "--endpoint", "198.51.100.10:51820", "--site", "hosting"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := overlayPlace(ctx, open.inventory, []string{"anchor", "--nothing"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
held := placementOf(t, ctx, open.inventory, "anchor")
|
||||
if held.Endpoint != "" || held.Site != "" || held.Hub {
|
||||
t.Fatalf("--nothing did not place it with nothing: %+v", held)
|
||||
}
|
||||
}
|
||||
|
||||
// Both at once cannot be meant, so neither silently wins.
|
||||
func TestAPlacementAndNothingTogetherIsRefused(t *testing.T) {
|
||||
open := aMesh(t)
|
||||
ctx := t.Context()
|
||||
if err := overlayPlace(ctx, open.inventory,
|
||||
[]string{"anchor", "--site", "hosting", "--nothing"}); err == nil {
|
||||
t.Fatal("a placement and --nothing together was accepted")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user