Skip to content

Commit 7f8f73b

Browse files
committed
test(mesh): document empty-location topology behavior
Add TestNewTopologyEmptyLocation covering LogicalGranularity and CrossGranularity with a node that has valid InternalIP but empty Location. Verifies unlabeled nodes are grouped into the shared location segment rather than isolated. Signed-off-by: Arsolitt <arsolitt@gmail.com>
1 parent 88d466a commit 7f8f73b

2 files changed

Lines changed: 203 additions & 2 deletions

File tree

pkg/mesh/topology.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,6 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
132132
switch granularity {
133133
case LogicalGranularity:
134134
location = logicalLocationPrefix + node.Location
135-
// Put node in a different location, if no private
136-
// IP was found.
137135
if node.InternalIP == nil {
138136
location = nodeLocationPrefix + node.Name
139137
}

pkg/mesh/topology_test.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ var (
4141
key3 = wgtypes.Key{'k', 'e', 'y', '3'}
4242
key4 = wgtypes.Key{'k', 'e', 'y', '4'}
4343
key5 = wgtypes.Key{'k', 'e', 'y', '5'}
44+
key6 = wgtypes.Key{'k', 'e', 'y', '6'}
4445
)
4546

4647
func setup(t *testing.T) (map[string]*Node, map[string]*Peer, wgtypes.Key, int) {
@@ -854,6 +855,208 @@ func TestNewTopology(t *testing.T) {
854855
}
855856
}
856857

858+
func TestNewTopologyEmptyLocation(t *testing.T) {
859+
nodes, peers, key, port := setup(t)
860+
861+
e5 := &net.IPNet{IP: net.ParseIP("10.1.0.5").To4(), Mask: net.CIDRMask(16, 32)}
862+
i5 := &net.IPNet{IP: net.ParseIP("10.1.0.5").To4(), Mask: net.CIDRMask(32, 32)}
863+
nodes["e"] = &Node{
864+
Name: "e",
865+
Endpoint: wireguard.NewEndpoint(e5.IP, DefaultKiloPort),
866+
InternalIP: i5,
867+
Location: "",
868+
Subnet: &net.IPNet{IP: net.ParseIP("10.2.5.0"), Mask: net.CIDRMask(24, 32)},
869+
Key: key6,
870+
}
871+
872+
// LogicalGranularity: node e has empty Location and a valid InternalIP.
873+
// Because Location == "", it is grouped under "location:" (logicalLocationPrefix + "").
874+
//
875+
// Segment sort order (location string):
876+
// location: → node e → w1 (Location="" with InternalIP set)
877+
// location:1 → node a → w2
878+
// location:2 → nodes b, c → w3
879+
// node:d → node d → w4 (Location="1" but InternalIP==nil)
880+
w1 := net.ParseIP("10.4.0.1").To4()
881+
w2 := net.ParseIP("10.4.0.2").To4()
882+
w3 := net.ParseIP("10.4.0.3").To4()
883+
w4 := net.ParseIP("10.4.0.4").To4()
884+
w5 := net.ParseIP("10.4.0.5").To4()
885+
886+
for _, tc := range []struct {
887+
name string
888+
granularity Granularity
889+
hostname string
890+
result *Topology
891+
}{
892+
{
893+
name: "logical from e (empty location)",
894+
granularity: LogicalGranularity,
895+
hostname: nodes["e"].Name,
896+
result: &Topology{
897+
hostname: nodes["e"].Name,
898+
leader: true,
899+
location: logicalLocationPrefix + nodes["e"].Location,
900+
subnet: nodes["e"].Subnet,
901+
privateIP: nodes["e"].InternalIP,
902+
wireGuardCIDR: &net.IPNet{IP: w1, Mask: net.CIDRMask(16, 32)},
903+
segments: []*segment{
904+
{
905+
allowedIPs: []net.IPNet{*nodes["e"].Subnet, *nodes["e"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
906+
endpoint: nodes["e"].Endpoint,
907+
key: nodes["e"].Key,
908+
persistentKeepalive: nodes["e"].PersistentKeepalive,
909+
location: logicalLocationPrefix + nodes["e"].Location,
910+
cidrs: []*net.IPNet{nodes["e"].Subnet},
911+
hostnames: []string{"e"},
912+
privateIPs: []net.IP{nodes["e"].InternalIP.IP},
913+
cniCompatibilityIPs: []*net.IPNet{nil},
914+
wireGuardIP: w1,
915+
},
916+
{
917+
allowedIPs: []net.IPNet{*nodes["a"].Subnet, *nodes["a"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
918+
endpoint: nodes["a"].Endpoint,
919+
key: nodes["a"].Key,
920+
persistentKeepalive: nodes["a"].PersistentKeepalive,
921+
location: logicalLocationPrefix + nodes["a"].Location,
922+
cidrs: []*net.IPNet{nodes["a"].Subnet},
923+
hostnames: []string{"a"},
924+
privateIPs: []net.IP{nodes["a"].InternalIP.IP},
925+
cniCompatibilityIPs: []*net.IPNet{nil},
926+
wireGuardIP: w2,
927+
},
928+
{
929+
allowedIPs: []net.IPNet{*nodes["b"].Subnet, *nodes["b"].InternalIP, *nodes["c"].Subnet, *nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
930+
endpoint: nodes["b"].Endpoint,
931+
key: nodes["b"].Key,
932+
persistentKeepalive: nodes["b"].PersistentKeepalive,
933+
location: logicalLocationPrefix + nodes["b"].Location,
934+
cidrs: []*net.IPNet{nodes["b"].Subnet, nodes["c"].Subnet},
935+
hostnames: []string{"b", "c"},
936+
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
937+
cniCompatibilityIPs: []*net.IPNet{nil, nil},
938+
wireGuardIP: w3,
939+
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
940+
},
941+
{
942+
allowedIPs: []net.IPNet{*nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
943+
endpoint: nodes["d"].Endpoint,
944+
key: nodes["d"].Key,
945+
persistentKeepalive: nodes["d"].PersistentKeepalive,
946+
location: nodeLocationPrefix + nodes["d"].Name,
947+
cidrs: []*net.IPNet{nodes["d"].Subnet},
948+
hostnames: []string{"d"},
949+
privateIPs: nil,
950+
cniCompatibilityIPs: []*net.IPNet{nil},
951+
wireGuardIP: w4,
952+
},
953+
},
954+
peers: []*Peer{peers["a"], peers["b"]},
955+
logger: log.NewNopLogger(),
956+
},
957+
},
958+
{
959+
// CrossGranularity: every node gets its own segment keyed by node name.
960+
// For node e with empty Location, nodeLocation must be "location:" (logicalLocationPrefix + "").
961+
//
962+
// Segment sort order (location = node:<name>):
963+
// node:a → w1, node:b → w2, node:c → w3, node:d → w4, node:e → w5
964+
name: "cross from e (empty location)",
965+
granularity: CrossGranularity,
966+
hostname: nodes["e"].Name,
967+
result: &Topology{
968+
hostname: nodes["e"].Name,
969+
leader: true,
970+
location: nodeLocationPrefix + nodes["e"].Name,
971+
nodeLocation: logicalLocationPrefix + nodes["e"].Location,
972+
subnet: nodes["e"].Subnet,
973+
privateIP: nodes["e"].InternalIP,
974+
wireGuardCIDR: &net.IPNet{IP: w5, Mask: net.CIDRMask(16, 32)},
975+
segments: []*segment{
976+
{
977+
allowedIPs: []net.IPNet{*nodes["a"].Subnet, *nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
978+
endpoint: nodes["a"].Endpoint,
979+
key: nodes["a"].Key,
980+
persistentKeepalive: nodes["a"].PersistentKeepalive,
981+
location: nodeLocationPrefix + nodes["a"].Name,
982+
nodeLocation: logicalLocationPrefix + nodes["a"].Location,
983+
cidrs: []*net.IPNet{nodes["a"].Subnet},
984+
hostnames: []string{"a"},
985+
privateIPs: []net.IP{nodes["a"].InternalIP.IP},
986+
cniCompatibilityIPs: []*net.IPNet{nil},
987+
wireGuardIP: w1,
988+
},
989+
{
990+
allowedIPs: []net.IPNet{*nodes["b"].Subnet, *nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
991+
endpoint: nodes["b"].Endpoint,
992+
key: nodes["b"].Key,
993+
persistentKeepalive: nodes["b"].PersistentKeepalive,
994+
location: nodeLocationPrefix + nodes["b"].Name,
995+
nodeLocation: logicalLocationPrefix + nodes["b"].Location,
996+
cidrs: []*net.IPNet{nodes["b"].Subnet},
997+
hostnames: []string{"b"},
998+
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
999+
cniCompatibilityIPs: []*net.IPNet{nil},
1000+
wireGuardIP: w2,
1001+
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
1002+
},
1003+
{
1004+
allowedIPs: []net.IPNet{*nodes["c"].Subnet, *nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
1005+
endpoint: nodes["c"].Endpoint,
1006+
key: nodes["c"].Key,
1007+
persistentKeepalive: nodes["c"].PersistentKeepalive,
1008+
location: nodeLocationPrefix + nodes["c"].Name,
1009+
nodeLocation: logicalLocationPrefix + nodes["c"].Location,
1010+
cidrs: []*net.IPNet{nodes["c"].Subnet},
1011+
hostnames: []string{"c"},
1012+
privateIPs: []net.IP{nodes["c"].InternalIP.IP},
1013+
cniCompatibilityIPs: []*net.IPNet{nil},
1014+
wireGuardIP: w3,
1015+
},
1016+
{
1017+
allowedIPs: []net.IPNet{*nodes["d"].Subnet, {IP: w4, Mask: net.CIDRMask(32, 32)}},
1018+
endpoint: nodes["d"].Endpoint,
1019+
key: nodes["d"].Key,
1020+
persistentKeepalive: nodes["d"].PersistentKeepalive,
1021+
location: nodeLocationPrefix + nodes["d"].Name,
1022+
nodeLocation: logicalLocationPrefix + nodes["d"].Location,
1023+
cidrs: []*net.IPNet{nodes["d"].Subnet},
1024+
hostnames: []string{"d"},
1025+
privateIPs: nil,
1026+
cniCompatibilityIPs: []*net.IPNet{nil},
1027+
wireGuardIP: w4,
1028+
},
1029+
{
1030+
allowedIPs: []net.IPNet{*nodes["e"].Subnet, *nodes["e"].InternalIP, {IP: w5, Mask: net.CIDRMask(32, 32)}},
1031+
endpoint: nodes["e"].Endpoint,
1032+
key: nodes["e"].Key,
1033+
persistentKeepalive: nodes["e"].PersistentKeepalive,
1034+
location: nodeLocationPrefix + nodes["e"].Name,
1035+
nodeLocation: logicalLocationPrefix + nodes["e"].Location,
1036+
cidrs: []*net.IPNet{nodes["e"].Subnet},
1037+
hostnames: []string{"e"},
1038+
privateIPs: []net.IP{nodes["e"].InternalIP.IP},
1039+
cniCompatibilityIPs: []*net.IPNet{nil},
1040+
wireGuardIP: w5,
1041+
},
1042+
},
1043+
peers: []*Peer{peers["a"], peers["b"]},
1044+
logger: log.NewNopLogger(),
1045+
},
1046+
},
1047+
} {
1048+
tc.result.key = key
1049+
tc.result.port = port
1050+
topo, err := NewTopology(nodes, peers, tc.granularity, tc.hostname, port, key, DefaultKiloSubnet, nil, 0, nil)
1051+
if err != nil {
1052+
t.Errorf("test case %q: failed to generate Topology: %v", tc.name, err)
1053+
}
1054+
if diff := pretty.Compare(topo, tc.result); diff != "" {
1055+
t.Errorf("test case %q: got diff: %v", tc.name, diff)
1056+
}
1057+
}
1058+
}
1059+
8571060
func mustTopo(t *testing.T, nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port int, key wgtypes.Key, subnet *net.IPNet, persistentKeepalive time.Duration) *Topology {
8581061
topo, err := NewTopology(nodes, peers, granularity, hostname, port, key, subnet, nil, persistentKeepalive, nil)
8591062
if err != nil {

0 commit comments

Comments
 (0)