policy,state: key the peer map by node ID

Adjacency becomes immutable, so a snapshot can resolve peers through its
own fresh views instead of storing them.

Updates #3417
This commit is contained in:
Kristoffer Dalby
2026-09-09 14:41:27 +00:00
parent f9f31c5e08
commit 95ba787417
8 changed files with 96 additions and 108 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ type PolicyManager interface {
// MatchersForNode returns matchers for peer relationship determination (unreduced)
MatchersForNode(node types.NodeView) ([]matcher.Match, error)
// BuildPeerMap constructs peer relationship maps for the given nodes
BuildPeerMap(nodes views.Slice[types.NodeView]) map[types.NodeID][]types.NodeView
BuildPeerMap(nodes views.Slice[types.NodeView]) map[types.NodeID][]types.NodeID
SSHPolicy(baseURL string, node types.NodeView) (*tailcfg.SSHPolicy, error)
// SSHCheckParams resolves the SSH check period for a (src, dst) pair
// from the current policy, avoiding trust of client-provided URL params.
+2 -2
View File
@@ -90,8 +90,8 @@ func TestIssue3212AutogroupInternetExitVisibility(t *testing.T) {
map[types.NodeID]struct{},
len(peerMap[srcNode.ID]),
)
for _, p := range peerMap[srcNode.ID] {
peerIDs[p.ID()] = struct{}{}
for _, id := range peerMap[srcNode.ID] {
peerIDs[id] = struct{}{}
}
for _, exitName := range exitNames {
+2 -6
View File
@@ -71,15 +71,11 @@ func TestIssue3233ViaInternetExitVisibility(t *testing.T) {
peerMap := pm.BuildPeerMap(nodes.ViewSlice())
require.True(t,
slices.ContainsFunc(peerMap[aliceLaptop.ID], func(n types.NodeView) bool {
return n.ID() == aliceExit.ID
}),
slices.Contains(peerMap[aliceLaptop.ID], aliceExit.ID),
"alice must see her tag:exit1 exit node as a peer")
require.False(t,
slices.ContainsFunc(peerMap[aliceLaptop.ID], func(n types.NodeView) bool {
return n.ID() == bobExit.ID
}),
slices.Contains(peerMap[aliceLaptop.ID], bobExit.ID),
"alice must not see bob's tag:exit2 exit node — via grant scopes to tag:exit1")
})
+7 -7
View File
@@ -603,7 +603,7 @@ func (pm *PolicyManager) Filter() ([]tailcfg.FilterRule, []matcher.Match) {
// up with doing the full work for every node O(n^2), while this will reduce
// the list as we see relationships while building the map, making it
// O(n^2/2) in the end, but with less work per node.
func (pm *PolicyManager) BuildPeerMap(nodes views.Slice[types.NodeView]) map[types.NodeID][]types.NodeView {
func (pm *PolicyManager) BuildPeerMap(nodes views.Slice[types.NodeView]) map[types.NodeID][]types.NodeID {
if pm == nil {
return nil
}
@@ -627,7 +627,7 @@ func (pm *PolicyManager) BuildPeerMap(nodes views.Slice[types.NodeView]) map[typ
// Via grants require the per-node path because the global filter
// skips via grants (compileFilterRules: if len(grant.Via) > 0 { continue }).
if !pm.needsPerNodeFilter {
ret := make(map[types.NodeID][]types.NodeView, nodes.Len())
ret := make(map[types.NodeID][]types.NodeID, nodes.Len())
// Build the map of all peers according to the matchers.
for i := range nodes.Len() {
@@ -639,8 +639,8 @@ func (pm *PolicyManager) BuildPeerMap(nodes views.Slice[types.NodeView]) map[typ
ri, rj := routeInfo[nodes.At(i).ID()], routeInfo[nodes.At(j).ID()]
if nodes.At(i).CanAccessWithRoutes(pm.matchers, nodes.At(j), ri.subnet, rj.subnet, rj.isExit) ||
nodes.At(j).CanAccessWithRoutes(pm.matchers, nodes.At(i), rj.subnet, ri.subnet, ri.isExit) {
ret[nodes.At(i).ID()] = append(ret[nodes.At(i).ID()], nodes.At(j))
ret[nodes.At(j).ID()] = append(ret[nodes.At(j).ID()], nodes.At(i))
ret[nodes.At(i).ID()] = append(ret[nodes.At(i).ID()], nodes.At(j).ID())
ret[nodes.At(j).ID()] = append(ret[nodes.At(j).ID()], nodes.At(i).ID())
}
}
}
@@ -649,7 +649,7 @@ func (pm *PolicyManager) BuildPeerMap(nodes views.Slice[types.NodeView]) map[typ
}
// For autogroup:self or via grants, build per-node peer relationships
ret := make(map[types.NodeID][]types.NodeView, nodes.Len())
ret := make(map[types.NodeID][]types.NodeID, nodes.Len())
// Pre-compute per-node matchers using unreduced compiled rules
// We need unreduced rules to determine peer relationships correctly.
@@ -692,8 +692,8 @@ func (pm *PolicyManager) BuildPeerMap(nodes views.Slice[types.NodeView]) map[typ
canIReachJ := hasFilterJ && nodeI.CanAccessWithRoutes(matchersJ, nodeJ, riI.subnet, riJ.subnet, riJ.isExit)
if canIAccessJ || canJAccessI || canJReachI || canIReachJ {
ret[nodeI.ID()] = append(ret[nodeI.ID()], nodeJ)
ret[nodeJ.ID()] = append(ret[nodeJ.ID()], nodeI)
ret[nodeI.ID()] = append(ret[nodeI.ID()], nodeJ.ID())
ret[nodeJ.ID()] = append(ret[nodeJ.ID()], nodeI.ID())
}
}
}
+22 -49
View File
@@ -741,9 +741,8 @@ func TestAutogroupSelfWithOtherRules(t *testing.T) {
test1Peers := peerMap[test1Node.ID]
// Verify test-1 can see the router (group:home -> tag:node-router rule)
require.True(t, slices.ContainsFunc(test1Peers, func(n types.NodeView) bool {
return n.ID() == test2RouterNode.ID
}), "test-1 should see test-2's router via group:home -> tag:node-router rule, even when autogroup:self rule exists (issue #2838)")
require.True(t, slices.Contains(test1Peers, test2RouterNode.ID),
"test-1 should see test-2's router via group:home -> tag:node-router rule, even when autogroup:self rule exists (issue #2838)")
// Verify that test-1 has filter rules (including autogroup:self and tag:node-router access)
rules, err := pm.FilterForNode(test1Node.View())
@@ -903,12 +902,12 @@ func TestTagPropagationToPeerMap(t *testing.T) {
// Check user2's peers - should include user1
user2Peers := initialPeerMap[user2Node.ID]
require.Len(t, user2Peers, 1, "user2 should have 1 peer initially (user1 with tag:web)")
require.Equal(t, user1Node.ID, user2Peers[0].ID(), "user2's peer should be user1")
require.Equal(t, user1Node.ID, user2Peers[0], "user2's peer should be user1")
// Check user1's peers - should include user2 (bidirectional ACL)
user1Peers := initialPeerMap[user1Node.ID]
require.Len(t, user1Peers, 1, "user1 should have 1 peer initially (user2)")
require.Equal(t, user2Node.ID, user1Peers[0].ID(), "user1's peer should be user2")
require.Equal(t, user2Node.ID, user1Peers[0], "user1's peer should be user2")
// Now change user1's tags: remove tag:web, keep only tag:internal
user1NodeUpdated := &types.Node{
@@ -1035,17 +1034,15 @@ func TestAutogroupSelfWithAdminOverride(t *testing.T) {
// Admin should see the tagged server as a peer (via group:admin -> *:* rule)
adminPeers := peerMap[adminNode.ID]
require.True(t, slices.ContainsFunc(adminPeers, func(n types.NodeView) bool {
return n.ID() == user1TaggedNode.ID
}), "admin should see tagged server as peer via *:* rule (issue #2990)")
require.True(t, slices.Contains(adminPeers, user1TaggedNode.ID),
"admin should see tagged server as peer via *:* rule (issue #2990)")
// Tagged server should also see admin as a peer (symmetric visibility)
// Even though tagged server cannot ACCESS admin, it should still SEE admin
// because admin CAN access it. This is required for proper network operation.
taggedPeers := peerMap[user1TaggedNode.ID]
require.True(t, slices.ContainsFunc(taggedPeers, func(n types.NodeView) bool {
return n.ID() == adminNode.ID
}), "tagged server should see admin as peer (symmetric visibility)")
require.True(t, slices.Contains(taggedPeers, adminNode.ID),
"tagged server should see admin as peer (symmetric visibility)")
}
// TestAutogroupSelfSymmetricVisibility verifies that peer visibility is symmetric:
@@ -1108,16 +1105,14 @@ func TestAutogroupSelfSymmetricVisibility(t *testing.T) {
// Device A (user1) should see device B (tag:web) as peer
aPeers := peerMap[deviceA.ID]
require.True(t, slices.ContainsFunc(aPeers, func(n types.NodeView) bool {
return n.ID() == deviceB.ID
}), "device A should see device B as peer (user1 -> tag:web rule)")
require.True(t, slices.Contains(aPeers, deviceB.ID),
"device A should see device B as peer (user1 -> tag:web rule)")
// Device B (tag:web) should ALSO see device A as peer (symmetric visibility)
// Even though B cannot ACCESS A, B should still SEE A as a peer
bPeers := peerMap[deviceB.ID]
require.True(t, slices.ContainsFunc(bPeers, func(n types.NodeView) bool {
return n.ID() == deviceA.ID
}), "device B should see device A as peer (symmetric visibility)")
require.True(t, slices.Contains(bPeers, deviceA.ID),
"device B should see device A as peer (symmetric visibility)")
}
// TestAutogroupSelfDoesNotBreakOtherUsersAccess reproduces the Discord scenario
@@ -1253,11 +1248,7 @@ func TestAutogroupSelfDoesNotBreakOtherUsersAccess(t *testing.T) {
// Helper to check if node A sees node B
canSee := func(a, b types.NodeID) bool {
peers := peerMap[a]
return slices.ContainsFunc(peers, func(n types.NodeView) bool {
return n.ID() == b
})
return slices.Contains(peerMap[a], b)
}
// Superadmin should see all tagged servers
@@ -1357,16 +1348,14 @@ func TestEmptyFilterNodesStillVisible(t *testing.T) {
// Admin should see the tagged server
adminPeers := peerMap[adminDevice.ID]
require.True(t, slices.ContainsFunc(adminPeers, func(n types.NodeView) bool {
return n.ID() == taggedServer.ID
}), "admin should see tagged server")
require.True(t, slices.Contains(adminPeers, taggedServer.ID),
"admin should see tagged server")
// Tagged server should see admin (symmetric visibility)
// Even though the server has no outbound rules (empty filter)
serverPeers := peerMap[taggedServer.ID]
require.True(t, slices.ContainsFunc(serverPeers, func(n types.NodeView) bool {
return n.ID() == adminDevice.ID
}), "tagged server should see admin (symmetric visibility)")
require.True(t, slices.Contains(serverPeers, adminDevice.ID),
"tagged server should see admin (symmetric visibility)")
}
// TestAutogroupSelfCombinedWithTags verifies that autogroup:self combined with
@@ -1434,11 +1423,7 @@ func TestAutogroupSelfCombinedWithTags(t *testing.T) {
// Helper to check visibility
canSee := func(a, b types.NodeID) bool {
peers := peerMap[a]
return slices.ContainsFunc(peers, func(n types.NodeView) bool {
return n.ID() == b
})
return slices.Contains(peerMap[a], b)
}
// Admin laptop should see: admin phone (autogroup:self) AND web server (tag:web)
@@ -1527,11 +1512,7 @@ func TestIssue2990SameUserTaggedDevice(t *testing.T) {
peerMap := pm.BuildPeerMap(nodes.ViewSlice())
canSee := func(a, b types.NodeID) bool {
peers := peerMap[a]
return slices.ContainsFunc(peers, func(n types.NodeView) bool {
return n.ID() == b
})
return slices.Contains(peerMap[a], b)
}
// node1 should see node2 (via group:admin -> *:* and symmetric visibility)
@@ -2205,9 +2186,7 @@ func TestBuildPeerMap_AutogroupInternetMakesExitNodeVisible(t *testing.T) {
peerMap := pm.BuildPeerMap(nodes.ViewSlice())
require.True(t,
slices.ContainsFunc(peerMap[aliceNode.ID], func(n types.NodeView) bool {
return n.ID() == exitNode.ID
}),
slices.Contains(peerMap[aliceNode.ID], exitNode.ID),
"alice should see the exit node as a peer when an ACL grants autogroup:internet (#3212)")
_, matchers := pm.Filter()
@@ -2527,12 +2506,6 @@ func TestPeerRelayGrantMakesRelayVisible(t *testing.T) {
},
}
containsID := func(peers []types.NodeView, id types.NodeID) bool {
return slices.ContainsFunc(peers, func(nv types.NodeView) bool {
return nv.ID() == id
})
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pm, err := NewPolicyManager(
@@ -2543,10 +2516,10 @@ func TestPeerRelayGrantMakesRelayVisible(t *testing.T) {
peerMap := pm.BuildPeerMap(tt.nodes.ViewSlice())
for _, srcID := range tt.srcIDs {
require.True(t, containsID(peerMap[srcID], tt.relayID),
require.True(t, slices.Contains(peerMap[srcID], tt.relayID),
"node %d must see relay %d via cap/relay alone",
srcID, tt.relayID)
require.True(t, containsID(peerMap[tt.relayID], srcID),
require.True(t, slices.Contains(peerMap[tt.relayID], srcID),
"relay %d must see node %d via cap/relay alone",
tt.relayID, srcID)
}
+28 -9
View File
@@ -148,13 +148,18 @@ func NewNodeStore(allNodes types.Nodes, peersFunc PeersFunc, batchSize int, batc
type Snapshot struct {
// nodesByID is the main source of truth for nodes.
nodesByID map[types.NodeID]types.Node
// nodeViewsByID resolves peer adjacency IDs to immutable views from this
// snapshot without rebuilding every peer slice on each write.
nodeViewsByID map[types.NodeID]types.NodeView
// calculated from nodesByID
nodesByNodeKey map[key.NodePublic]types.NodeView
nodesByMachineKey map[key.MachinePublic]map[types.UserID]types.NodeView
peersByNode map[types.NodeID][]types.NodeView
nodesByUser map[types.UserID][]types.NodeView
allNodes []types.NodeView
// peersByNode stores immutable adjacency as IDs; ListPeers resolves the
// corresponding views through nodeViewsByID.
peersByNode map[types.NodeID][]types.NodeID
nodesByUser map[types.UserID][]types.NodeView
allNodes []types.NodeView
// routes maps each prefix to its current primary advertiser. The
// previous assignment is carried over when still valid so the
@@ -167,7 +172,7 @@ type Snapshot struct {
// with the relationships between nodes and their peers.
// This will typically be used to calculate which nodes can see each other
// based on the current policy.
type PeersFunc func(nodes []types.NodeView) map[types.NodeID][]types.NodeView
type PeersFunc func(nodes []types.NodeView) map[types.NodeID][]types.NodeID
// work represents a single operation to be performed on the [NodeStore].
type work struct {
@@ -608,14 +613,19 @@ func snapshotFromNodes(
defer timer.ObserveDuration()
allNodes := make([]types.NodeView, 0, len(nodes))
nodeViewsByID := make(map[types.NodeID]types.NodeView, len(nodes))
for _, n := range nodes {
allNodes = append(allNodes, n.View())
nv := n.View()
allNodes = append(allNodes, nv)
nodeViewsByID[n.ID] = nv
}
routes, isPrimaryRoute := electPrimaryRoutes(nodes, prevRoutes)
newSnap := Snapshot{
nodesByID: nodes,
nodeViewsByID: nodeViewsByID,
allNodes: allNodes,
nodesByNodeKey: make(map[key.NodePublic]types.NodeView),
nodesByMachineKey: make(map[key.MachinePublic]map[types.UserID]types.NodeView),
@@ -624,7 +634,7 @@ func snapshotFromNodes(
// it will use the list of all nodes, combined with the
// current policy to precalculate which nodes are peers and
// can see each other.
peersByNode: func() map[types.NodeID][]types.NodeView {
peersByNode: func() map[types.NodeID][]types.NodeID {
peersTimer := prometheus.NewTimer(nodeStorePeersCalculationDuration)
defer peersTimer.ObserveDuration()
@@ -638,7 +648,7 @@ func snapshotFromNodes(
// Build nodesByUser, nodesByNodeKey, and nodesByMachineKey maps
for _, n := range nodes {
nodeView := n.View()
nodeView := nodeViewsByID[n.ID]
userID := n.TypedUserID()
// Tagged nodes are owned by their tags, not a user,
@@ -649,7 +659,6 @@ func snapshotFromNodes(
newSnap.nodesByNodeKey[n.NodeKey] = nodeView
// Build machine key index
if newSnap.nodesByMachineKey[n.MachineKey] == nil {
newSnap.nodesByMachineKey[n.MachineKey] = make(map[types.UserID]types.NodeView)
}
@@ -882,7 +891,17 @@ func (s *NodeStore) ListPeers(id types.NodeID) views.Slice[types.NodeView] {
nodeStoreOperations.WithLabelValues("list_peers").Inc()
return views.SliceOf(s.data.Load().peersByNode[id])
snapshot := s.data.Load()
peerIDs := snapshot.peersByNode[id]
peers := make([]types.NodeView, 0, len(peerIDs))
for _, peerID := range peerIDs {
if peer, ok := snapshot.nodeViewsByID[peerID]; ok {
peers = append(peers, peer)
}
}
return views.SliceOf(peers)
}
// PrimaryRouteFor returns the current primary advertiser for prefix.
+33 -33
View File
@@ -26,8 +26,8 @@ func TestSnapshotFromNodes(t *testing.T) {
name: "empty nodes",
setupFunc: func() (map[types.NodeID]types.Node, PeersFunc) {
nodes := make(map[types.NodeID]types.Node)
peersFunc := func(nodes []types.NodeView) map[types.NodeID][]types.NodeView {
return make(map[types.NodeID][]types.NodeView)
peersFunc := func(nodes []types.NodeView) map[types.NodeID][]types.NodeID {
return make(map[types.NodeID][]types.NodeID)
}
return nodes, peersFunc
@@ -79,9 +79,9 @@ func TestSnapshotFromNodes(t *testing.T) {
// Each node sees the other as peer (but not itself)
assert.Len(t, snapshot.peersByNode[1], 1)
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[1][0].ID())
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[1][0])
assert.Len(t, snapshot.peersByNode[2], 1)
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[2][0].ID())
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[2][0])
assert.Len(t, snapshot.nodesByUser[1], 2)
},
},
@@ -133,17 +133,17 @@ func TestSnapshotFromNodes(t *testing.T) {
// Odd nodes should only see other odd nodes as peers
require.Len(t, snapshot.peersByNode[1], 1)
assert.Equal(t, types.NodeID(3), snapshot.peersByNode[1][0].ID())
assert.Equal(t, types.NodeID(3), snapshot.peersByNode[1][0])
require.Len(t, snapshot.peersByNode[3], 1)
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[3][0].ID())
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[3][0])
// Even nodes should only see other even nodes as peers
require.Len(t, snapshot.peersByNode[2], 1)
assert.Equal(t, types.NodeID(4), snapshot.peersByNode[2][0].ID())
assert.Equal(t, types.NodeID(4), snapshot.peersByNode[2][0])
require.Len(t, snapshot.peersByNode[4], 1)
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[4][0].ID())
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[4][0])
},
},
}
@@ -190,14 +190,14 @@ func createTestNode(nodeID types.NodeID, userID uint, username, hostname string)
// Peer functions
func allowAllPeersFunc(nodes []types.NodeView) map[types.NodeID][]types.NodeView {
ret := make(map[types.NodeID][]types.NodeView, len(nodes))
func allowAllPeersFunc(nodes []types.NodeView) map[types.NodeID][]types.NodeID {
ret := make(map[types.NodeID][]types.NodeID, len(nodes))
for _, node := range nodes {
var peers []types.NodeView
var peers []types.NodeID
for _, n := range nodes {
if n.ID() != node.ID() {
peers = append(peers, n)
peers = append(peers, n.ID())
}
}
@@ -207,10 +207,10 @@ func allowAllPeersFunc(nodes []types.NodeView) map[types.NodeID][]types.NodeView
return ret
}
func oddEvenPeersFunc(nodes []types.NodeView) map[types.NodeID][]types.NodeView {
ret := make(map[types.NodeID][]types.NodeView, len(nodes))
func oddEvenPeersFunc(nodes []types.NodeView) map[types.NodeID][]types.NodeID {
ret := make(map[types.NodeID][]types.NodeID, len(nodes))
for _, node := range nodes {
var peers []types.NodeView
var peers []types.NodeID
nodeIsOdd := node.ID()%2 == 1
@@ -223,7 +223,7 @@ func oddEvenPeersFunc(nodes []types.NodeView) map[types.NodeID][]types.NodeView
// Only add peer if both are odd or both are even
if nodeIsOdd == peerIsOdd {
peers = append(peers, n)
peers = append(peers, n.ID())
}
}
@@ -313,9 +313,9 @@ func TestNodeStoreOperations(t *testing.T) {
// Now both nodes should see each other as peers
assert.Len(t, snapshot.peersByNode[1], 1)
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[1][0].ID())
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[1][0])
assert.Len(t, snapshot.peersByNode[2], 1)
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[2][0].ID())
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[2][0])
assert.Len(t, snapshot.nodesByUser[1], 2)
},
},
@@ -382,9 +382,9 @@ func TestNodeStoreOperations(t *testing.T) {
// Remaining nodes should see each other as peers
assert.Len(t, snapshot.peersByNode[1], 1)
assert.Equal(t, types.NodeID(3), snapshot.peersByNode[1][0].ID())
assert.Equal(t, types.NodeID(3), snapshot.peersByNode[1][0])
assert.Len(t, snapshot.peersByNode[3], 1)
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[3][0].ID())
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[3][0])
// User groupings updated
assert.Len(t, snapshot.nodesByUser[1], 1) // user1 now has only node 1
@@ -475,16 +475,16 @@ func TestNodeStoreOperations(t *testing.T) {
// Verify odd-even peer relationships
require.Len(t, snapshot.peersByNode[1], 1)
assert.Equal(t, types.NodeID(3), snapshot.peersByNode[1][0].ID())
assert.Equal(t, types.NodeID(3), snapshot.peersByNode[1][0])
require.Len(t, snapshot.peersByNode[2], 1)
assert.Equal(t, types.NodeID(4), snapshot.peersByNode[2][0].ID())
assert.Equal(t, types.NodeID(4), snapshot.peersByNode[2][0])
require.Len(t, snapshot.peersByNode[3], 1)
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[3][0].ID())
assert.Equal(t, types.NodeID(1), snapshot.peersByNode[3][0])
require.Len(t, snapshot.peersByNode[4], 1)
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[4][0].ID())
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[4][0])
},
},
{
@@ -500,9 +500,9 @@ func TestNodeStoreOperations(t *testing.T) {
// Even nodes should still see each other
require.Len(t, snapshot.peersByNode[2], 1)
assert.Equal(t, types.NodeID(4), snapshot.peersByNode[2][0].ID())
assert.Equal(t, types.NodeID(4), snapshot.peersByNode[2][0])
require.Len(t, snapshot.peersByNode[4], 1)
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[4][0].ID())
assert.Equal(t, types.NodeID(2), snapshot.peersByNode[4][0])
},
},
},
@@ -1225,16 +1225,16 @@ func TestRebuildPeerMapsWithChangedPeersFunc(t *testing.T) {
// This simulates how PolicyManager.BuildPeerMap works - it reads state
// that can change between calls
dynamicPeersFunc := func(nodes []types.NodeView) map[types.NodeID][]types.NodeView {
ret := make(map[types.NodeID][]types.NodeView, len(nodes))
dynamicPeersFunc := func(nodes []types.NodeView) map[types.NodeID][]types.NodeID {
ret := make(map[types.NodeID][]types.NodeID, len(nodes))
if allowPeers {
// Allow all peers
for _, node := range nodes {
var peers []types.NodeView
var peers []types.NodeID
for _, n := range nodes {
if n.ID() != node.ID() {
peers = append(peers, n)
peers = append(peers, n.ID())
}
}
@@ -1243,7 +1243,7 @@ func TestRebuildPeerMapsWithChangedPeersFunc(t *testing.T) {
} else {
// Allow no peers
for _, node := range nodes {
ret[node.ID()] = []types.NodeView{}
ret[node.ID()] = []types.NodeID{}
}
}
@@ -1265,8 +1265,8 @@ func TestRebuildPeerMapsWithChangedPeersFunc(t *testing.T) {
snapshot := store.data.Load()
require.Len(t, snapshot.peersByNode[1], 1, "node1 should have 1 peer initially")
require.Len(t, snapshot.peersByNode[2], 1, "node2 should have 1 peer initially")
require.Equal(t, types.NodeID(2), snapshot.peersByNode[1][0].ID())
require.Equal(t, types.NodeID(1), snapshot.peersByNode[2][0].ID())
require.Equal(t, types.NodeID(2), snapshot.peersByNode[1][0])
require.Equal(t, types.NodeID(1), snapshot.peersByNode[2][0])
// Now "change the policy" by disabling peers
allowPeers = false
+1 -1
View File
@@ -272,7 +272,7 @@ func NewState(cfg *types.Config) (*State, error) {
// This moves the complex peer relationship logic into the policy package where it belongs.
nodeStore := NewNodeStore(
nodes,
func(nodes []types.NodeView) map[types.NodeID][]types.NodeView {
func(nodes []types.NodeView) map[types.NodeID][]types.NodeID {
return polMan.BuildPeerMap(views.SliceOf(nodes))
},
batchSize,