mesh/merge-gate head 19e3eac74658 merged into trunk 89cfa9431d8f — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head 19e3eac74658 merged into trunk 89cfa9431d8f — pass: its merge-check.sh passed
mesh/delivery delivered
124 lines
4.9 KiB
Go
124 lines
4.9 KiB
Go
package link
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
// Supersession as a check rather than a memory (design 25 §3).
|
|
//
|
|
// Holding a message in memory let the controller drop an older report when a newer one for the
|
|
// same node arrived. On the bus being built the message belongs to the server and comes back
|
|
// whatever happened meanwhile — so the older report is redelivered *after* the newer was applied,
|
|
// and acting on it would undo the newer.
|
|
//
|
|
// The answer was already in the message: a report carries the digest of the declaration it is
|
|
// about, so "is this the past?" is a question the message answers.
|
|
|
|
// sentAndHeard records reports and knows what was last sent, which is the pair the check needs.
|
|
type sentAndHeard struct {
|
|
sent string
|
|
heard []Report
|
|
err error
|
|
}
|
|
|
|
func (s *sentAndHeard) Heard(_ context.Context, r Report) (bool, error) {
|
|
if s.err != nil {
|
|
return false, s.err
|
|
}
|
|
s.heard = append(s.heard, r)
|
|
return true, nil
|
|
}
|
|
|
|
func (s *sentAndHeard) Outstanding(context.Context, string) (string, error) { return s.sent, nil }
|
|
|
|
// A report about a declaration the mesh has moved past is settled and not acted on. Settled rather
|
|
// than dropped, because there is nothing wrong with the message — it is simply the past, and
|
|
// redelivering it for ever is worse than letting it go.
|
|
func TestAReportAboutASupersededDeclarationIsNotActedOn(t *testing.T) {
|
|
store := &sentAndHeard{sent: "d2"}
|
|
s, in := serving()
|
|
s.listener = store
|
|
to := &settled{}
|
|
s.act(context.Background(), in.sends(t, to, KindReport, aReport("anchor", "d1")))
|
|
|
|
if len(store.heard) != 0 {
|
|
t.Fatalf("a report about a superseded declaration was acted on: %+v", store.heard)
|
|
}
|
|
if !to.acked {
|
|
t.Fatalf("a superseded report was not settled, so it comes back for ever: %+v", *to)
|
|
}
|
|
}
|
|
|
|
// The report about the declaration that *is* outstanding is acted on, and so is one from a node
|
|
// the mesh has no digest for — an older host that says nothing about which declaration it applied
|
|
// has nothing to be judged against, and refusing it would silence every node built before reports
|
|
// carried the digest.
|
|
func TestAReportAboutTheOutstandingDeclarationIsActedOn(t *testing.T) {
|
|
for _, c := range []struct{ what, sent, declared string }{
|
|
{"the one outstanding", "d2", "d2"},
|
|
{"a report that says nothing about which", "d2", ""},
|
|
{"a node nothing was ever sent", "", "d1"},
|
|
} {
|
|
store := &sentAndHeard{sent: c.sent}
|
|
s, in := serving()
|
|
s.listener = store
|
|
to := &settled{}
|
|
s.act(context.Background(), in.sends(t, to, KindReport, aReport("anchor", c.declared)))
|
|
if len(store.heard) != 1 || !to.acked {
|
|
t.Errorf("%s: was not acted on and acknowledged: heard %+v, settled %+v",
|
|
c.what, store.heard, *to)
|
|
}
|
|
}
|
|
}
|
|
|
|
// **Staleness is decided before the store is waited on**, not after: a redelivery that lost its
|
|
// race is not worth holding a slot in the window that a current message needs.
|
|
func TestASupersededReportIsNotHeldForTheStore(t *testing.T) {
|
|
store := &sentAndHeard{sent: "d2", err: errors.Join(ErrTryAgain, errors.New("starting up"))}
|
|
s, in := serving()
|
|
s.listener = store
|
|
to := &settled{}
|
|
s.act(context.Background(), in.sends(t, to, KindReport, aReport("anchor", "d1")))
|
|
if !to.acked || len(in.held) != 0 {
|
|
t.Fatalf("a superseded report waited for the store: %+v, %d held", *to, len(in.held))
|
|
}
|
|
}
|
|
|
|
// **Half of a report is not about a declaration, and that half is never stale.**
|
|
//
|
|
// What the machine *is* — the tunnel it took over, the ports its own bundle holds, what an adopted
|
|
// node found and is keeping, a node moving its overlay key — reaches the mesh on a report and
|
|
// nowhere else. A rekey set aside as stale is a node whose overlay key never moves, and no retry is
|
|
// coming, because the node said it once. So a report carrying any of these is acted on whenever it
|
|
// arrives, however far the mesh has moved on.
|
|
func TestAReportCarryingWhatOnlyTheNodeKnowsIsActedOnHoweverOldItIs(t *testing.T) {
|
|
for _, c := range []struct {
|
|
what string
|
|
report Report
|
|
}{
|
|
{"a rekey", Report{Node: "anchor", Declared: "d1",
|
|
Rekey: &Rekey{Previous: "k1", OverlayKey: "k2"}}},
|
|
{"the tunnel it carried", Report{Node: "anchor", Declared: "d1",
|
|
Tunnel: &CarriedTunnel{Interface: "wg0", State: "taken"}}},
|
|
{"what an adopted node holds", Report{Node: "anchor", Declared: "d1",
|
|
Held: []Held{{ID: "conf", Module: "web", Kind: "file"}}}},
|
|
{"the firewall it found", Report{Node: "anchor", Declared: "d1", Firewall: "ufw"}},
|
|
{"what is reachable on it", Report{Node: "anchor", Declared: "d1",
|
|
Reachable: []Reach{{Protocol: "tcp", Port: 443}}}},
|
|
{"the ports its own bundle holds", Report{Node: "anchor", Declared: "d1",
|
|
Carried: []int{5432}}},
|
|
} {
|
|
store := &sentAndHeard{sent: "d9"}
|
|
s, in := serving()
|
|
s.listener = store
|
|
to := &settled{}
|
|
s.act(context.Background(), in.sends(t, to, KindReport, c.report))
|
|
if len(store.heard) != 1 {
|
|
t.Errorf("%s was set aside as stale, and the mesh will never hear it again: %+v",
|
|
c.what, *to)
|
|
}
|
|
}
|
|
}
|