musl takes the first reply from any listed nameserver, so a public fallback beside the mesh's resolver answered NXDOMAIN for mesh names in every Alpine container (hq ADR 0223). The fix is two mesh resolvers and no public one, which needs mesh-dns-resolver held on two machines: a seat can now be replicated, each holder recorded by 'seat <name> --add', checkClaims accepts every holder on record and still refuses a second holder of any other mesh seat, a holder answers its own requirement, and a roster fact gives each replicated seat's holders, this machine first, so resolv-conf can list them. Migration 0062 keys a holding by seat and assignment.
154 lines
5.4 KiB
Go
154 lines
5.4 KiB
Go
package inventory
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-controller/internal/catalogue"
|
|
)
|
|
|
|
// A seat's holder is a row, changed as one act (novox/hq ADR 0131). What these pin is the shape of
|
|
// that row's life: it needs an assignment to point at, it is replaced rather than added to, and it
|
|
// goes when the assignment does — so a seat never points at something that is not running anywhere.
|
|
|
|
func twoBrokersOnTwoNodes(t *testing.T) (*Inventory, context.Context) {
|
|
t.Helper()
|
|
old := catalogue.Manifest{Module: "old-broker", Version: "1",
|
|
Provides: []catalogue.Offer{{Name: "mesh-bus", Scope: catalogue.ScopeMesh}},
|
|
Claims: []catalogue.Claim{{Name: "mesh-broker", Scope: catalogue.ScopeMesh}}}
|
|
new := catalogue.Manifest{Module: "new-broker", Version: "1",
|
|
Provides: []catalogue.Offer{{Name: "mesh-bus", Scope: catalogue.ScopeMesh}},
|
|
Claims: []catalogue.Claim{{Name: "mesh-broker", Scope: catalogue.ScopeMesh}}}
|
|
inv, ctx := aMeshWith(t, old, new)
|
|
// The holding references the seat's row, which `migrate` seeds on a real mesh.
|
|
if _, err := inv.SeedSeats(ctx, catalogue.DefaultSeats()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, n := range []string{"anchor", "laptop"} {
|
|
if _, err := inv.AddNode(ctx, n); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if _, err := inv.Assign(ctx, "anchor", "old-broker"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := inv.Assign(ctx, "laptop", "new-broker"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return inv, ctx
|
|
}
|
|
|
|
func TestAHandoverIsOneRowReplacedNotOneAdded(t *testing.T) {
|
|
inv, ctx := twoBrokersOnTwoNodes(t)
|
|
|
|
if err := inv.HoldSeat(ctx, "mesh-broker", catalogue.ScopeMesh, "anchor", "old-broker"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.HoldSeat(ctx, "mesh-broker", catalogue.ScopeMesh, "laptop", "new-broker"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
held, err := inv.Holdings(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(held) != 1 || held[0].Node != "laptop" || held[0].Module != "new-broker" || held[0].Scope != catalogue.ScopeMesh {
|
|
t.Fatalf("after a handover the seat is not held by exactly the new holder: %+v", held)
|
|
}
|
|
}
|
|
|
|
func TestASeatCannotBeHandedToSomethingNotAssigned(t *testing.T) {
|
|
inv, ctx := twoBrokersOnTwoNodes(t)
|
|
// new-broker is assigned to laptop, not anchor.
|
|
if err := inv.HoldSeat(ctx, "mesh-broker", catalogue.ScopeMesh, "anchor", "new-broker"); err == nil {
|
|
t.Fatal("a seat was handed to a module not assigned where it was named")
|
|
}
|
|
}
|
|
|
|
func TestUnassigningTheHolderTakesTheHoldingWithIt(t *testing.T) {
|
|
inv, ctx := twoBrokersOnTwoNodes(t)
|
|
if err := inv.HoldSeat(ctx, "mesh-broker", catalogue.ScopeMesh, "laptop", "new-broker"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.Unassign(ctx, "laptop", "new-broker"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
held, err := inv.Holdings(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(held) != 0 {
|
|
t.Fatalf("the holding outlived the assignment it pointed at: %+v", held)
|
|
}
|
|
}
|
|
|
|
func TestAMachinesMembershipIsOneRowReplacedAndGoesWithTheMachine(t *testing.T) {
|
|
inv, ctx := twoBrokersOnTwoNodes(t)
|
|
if err := inv.PutBusMembership(ctx, "anchor", "first"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.PutBusMembership(ctx, "anchor", "second"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := inv.BusMemberships(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got["anchor"] != "second" || len(got) != 1 {
|
|
t.Fatalf("a re-told membership did not replace the first: %v", got)
|
|
}
|
|
}
|
|
|
|
// A replicated seat has several holders on record (novox/hq ADR 0223): each is added beside the
|
|
// others, adding one twice changes nothing, a handover still leaves exactly one, and unassigning one
|
|
// takes only its own row.
|
|
func TestAReplicatedSeatHasSeveralHoldersOnRecord(t *testing.T) {
|
|
resolver := catalogue.Manifest{Module: "resolver", Version: "1",
|
|
Provides: []catalogue.Offer{{Name: "wildcard-resolution", Scope: catalogue.ScopeMesh}},
|
|
Claims: []catalogue.Claim{{Name: "mesh-dns-resolver", Scope: catalogue.ScopeMesh}}}
|
|
inv, ctx := aMeshWith(t, resolver)
|
|
if _, err := inv.SeedSeats(ctx, catalogue.DefaultSeats()); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, n := range []string{"anchor", "home"} {
|
|
if _, err := inv.AddNode(ctx, n); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := inv.Assign(ctx, n, "resolver"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := inv.HoldSeat(ctx, "mesh-dns-resolver", catalogue.ScopeMesh, "anchor", "resolver"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for range 2 {
|
|
if err := inv.AddSeatHolder(ctx, "mesh-dns-resolver", catalogue.ScopeMesh, "home", "resolver"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
held, err := inv.Holdings(ctx)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(held) != 2 || held[0].Node != "anchor" || held[1].Node != "home" {
|
|
t.Fatalf("the two holders are not both on record, once each: %+v", held)
|
|
}
|
|
if err := inv.Unassign(ctx, "home", "resolver"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if held, _ = inv.Holdings(ctx); len(held) != 1 || held[0].Node != "anchor" {
|
|
t.Fatalf("unassigning one holder took more or less than its own row: %+v", held)
|
|
}
|
|
if _, err := inv.Assign(ctx, "home", "resolver"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.AddSeatHolder(ctx, "mesh-dns-resolver", catalogue.ScopeMesh, "home", "resolver"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := inv.HoldSeat(ctx, "mesh-dns-resolver", catalogue.ScopeMesh, "home", "resolver"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if held, _ = inv.Holdings(ctx); len(held) != 1 || held[0].Node != "home" {
|
|
t.Fatalf("a handover left other holders on record: %+v", held)
|
|
}
|
|
}
|