map_test.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. // Copyright (c) 2021 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package controlclient
  5. import (
  6. "encoding/json"
  7. "fmt"
  8. "reflect"
  9. "strings"
  10. "testing"
  11. "time"
  12. "tailscale.com/tailcfg"
  13. "tailscale.com/types/key"
  14. "tailscale.com/types/netmap"
  15. )
  16. func TestUndeltaPeers(t *testing.T) {
  17. defer func(old func() time.Time) { clockNow = old }(clockNow)
  18. var curTime time.Time
  19. clockNow = func() time.Time {
  20. return curTime
  21. }
  22. online := func(v bool) func(*tailcfg.Node) {
  23. return func(n *tailcfg.Node) {
  24. n.Online = &v
  25. }
  26. }
  27. seenAt := func(t time.Time) func(*tailcfg.Node) {
  28. return func(n *tailcfg.Node) {
  29. n.LastSeen = &t
  30. }
  31. }
  32. n := func(id tailcfg.NodeID, name string, mod ...func(*tailcfg.Node)) *tailcfg.Node {
  33. n := &tailcfg.Node{ID: id, Name: name}
  34. for _, f := range mod {
  35. f(n)
  36. }
  37. return n
  38. }
  39. peers := func(nv ...*tailcfg.Node) []*tailcfg.Node { return nv }
  40. tests := []struct {
  41. name string
  42. mapRes *tailcfg.MapResponse
  43. curTime time.Time
  44. prev []*tailcfg.Node
  45. want []*tailcfg.Node
  46. }{
  47. {
  48. name: "full_peers",
  49. mapRes: &tailcfg.MapResponse{
  50. Peers: peers(n(1, "foo"), n(2, "bar")),
  51. },
  52. want: peers(n(1, "foo"), n(2, "bar")),
  53. },
  54. {
  55. name: "full_peers_ignores_deltas",
  56. mapRes: &tailcfg.MapResponse{
  57. Peers: peers(n(1, "foo"), n(2, "bar")),
  58. PeersRemoved: []tailcfg.NodeID{2},
  59. },
  60. want: peers(n(1, "foo"), n(2, "bar")),
  61. },
  62. {
  63. name: "add_and_update",
  64. prev: peers(n(1, "foo"), n(2, "bar")),
  65. mapRes: &tailcfg.MapResponse{
  66. PeersChanged: peers(n(0, "zero"), n(2, "bar2"), n(3, "three")),
  67. },
  68. want: peers(n(0, "zero"), n(1, "foo"), n(2, "bar2"), n(3, "three")),
  69. },
  70. {
  71. name: "remove",
  72. prev: peers(n(1, "foo"), n(2, "bar")),
  73. mapRes: &tailcfg.MapResponse{
  74. PeersRemoved: []tailcfg.NodeID{1},
  75. },
  76. want: peers(n(2, "bar")),
  77. },
  78. {
  79. name: "add_and_remove",
  80. prev: peers(n(1, "foo"), n(2, "bar")),
  81. mapRes: &tailcfg.MapResponse{
  82. PeersChanged: peers(n(1, "foo2")),
  83. PeersRemoved: []tailcfg.NodeID{2},
  84. },
  85. want: peers(n(1, "foo2")),
  86. },
  87. {
  88. name: "unchanged",
  89. prev: peers(n(1, "foo"), n(2, "bar")),
  90. mapRes: &tailcfg.MapResponse{},
  91. want: peers(n(1, "foo"), n(2, "bar")),
  92. },
  93. {
  94. name: "online_change",
  95. prev: peers(n(1, "foo"), n(2, "bar")),
  96. mapRes: &tailcfg.MapResponse{
  97. OnlineChange: map[tailcfg.NodeID]bool{
  98. 1: true,
  99. },
  100. },
  101. want: peers(
  102. n(1, "foo", online(true)),
  103. n(2, "bar"),
  104. ),
  105. },
  106. {
  107. name: "online_change_offline",
  108. prev: peers(n(1, "foo"), n(2, "bar")),
  109. mapRes: &tailcfg.MapResponse{
  110. OnlineChange: map[tailcfg.NodeID]bool{
  111. 1: false,
  112. 2: true,
  113. },
  114. },
  115. want: peers(
  116. n(1, "foo", online(false)),
  117. n(2, "bar", online(true)),
  118. ),
  119. },
  120. {
  121. name: "peer_seen_at",
  122. prev: peers(n(1, "foo", seenAt(time.Unix(111, 0))), n(2, "bar")),
  123. curTime: time.Unix(123, 0),
  124. mapRes: &tailcfg.MapResponse{
  125. PeerSeenChange: map[tailcfg.NodeID]bool{
  126. 1: false,
  127. 2: true,
  128. },
  129. },
  130. want: peers(
  131. n(1, "foo"),
  132. n(2, "bar", seenAt(time.Unix(123, 0))),
  133. ),
  134. },
  135. }
  136. for _, tt := range tests {
  137. t.Run(tt.name, func(t *testing.T) {
  138. if !tt.curTime.IsZero() {
  139. curTime = tt.curTime
  140. }
  141. undeltaPeers(tt.mapRes, tt.prev)
  142. if !reflect.DeepEqual(tt.mapRes.Peers, tt.want) {
  143. t.Errorf("wrong results\n got: %s\nwant: %s", formatNodes(tt.mapRes.Peers), formatNodes(tt.want))
  144. }
  145. })
  146. }
  147. }
  148. func formatNodes(nodes []*tailcfg.Node) string {
  149. var sb strings.Builder
  150. for i, n := range nodes {
  151. if i > 0 {
  152. sb.WriteString(", ")
  153. }
  154. var extra string
  155. if n.Online != nil {
  156. extra += fmt.Sprintf(", online=%v", *n.Online)
  157. }
  158. if n.LastSeen != nil {
  159. extra += fmt.Sprintf(", lastSeen=%v", n.LastSeen.Unix())
  160. }
  161. fmt.Fprintf(&sb, "(%d, %q%s)", n.ID, n.Name, extra)
  162. }
  163. return sb.String()
  164. }
  165. func newTestMapSession(t *testing.T) *mapSession {
  166. return newMapSession(key.NewNode())
  167. }
  168. func TestNetmapForResponse(t *testing.T) {
  169. t.Run("implicit_packetfilter", func(t *testing.T) {
  170. somePacketFilter := []tailcfg.FilterRule{
  171. {
  172. SrcIPs: []string{"*"},
  173. DstPorts: []tailcfg.NetPortRange{
  174. {IP: "10.2.3.4", Ports: tailcfg.PortRange{First: 22, Last: 22}},
  175. },
  176. },
  177. }
  178. ms := newTestMapSession(t)
  179. nm1 := ms.netmapForResponse(&tailcfg.MapResponse{
  180. Node: new(tailcfg.Node),
  181. PacketFilter: somePacketFilter,
  182. })
  183. if len(nm1.PacketFilter) == 0 {
  184. t.Fatalf("zero length PacketFilter")
  185. }
  186. nm2 := ms.netmapForResponse(&tailcfg.MapResponse{
  187. Node: new(tailcfg.Node),
  188. PacketFilter: nil, // testing that the server can omit this.
  189. })
  190. if len(nm1.PacketFilter) == 0 {
  191. t.Fatalf("zero length PacketFilter in 2nd netmap")
  192. }
  193. if !reflect.DeepEqual(nm1.PacketFilter, nm2.PacketFilter) {
  194. t.Error("packet filters differ")
  195. }
  196. })
  197. t.Run("implicit_dnsconfig", func(t *testing.T) {
  198. someDNSConfig := &tailcfg.DNSConfig{Domains: []string{"foo", "bar"}}
  199. ms := newTestMapSession(t)
  200. nm1 := ms.netmapForResponse(&tailcfg.MapResponse{
  201. Node: new(tailcfg.Node),
  202. DNSConfig: someDNSConfig,
  203. })
  204. if !reflect.DeepEqual(nm1.DNS, *someDNSConfig) {
  205. t.Fatalf("1st DNS wrong")
  206. }
  207. nm2 := ms.netmapForResponse(&tailcfg.MapResponse{
  208. Node: new(tailcfg.Node),
  209. DNSConfig: nil, // implicit
  210. })
  211. if !reflect.DeepEqual(nm2.DNS, *someDNSConfig) {
  212. t.Fatalf("2nd DNS wrong")
  213. }
  214. })
  215. t.Run("collect_services", func(t *testing.T) {
  216. ms := newTestMapSession(t)
  217. var nm *netmap.NetworkMap
  218. wantCollect := func(v bool) {
  219. t.Helper()
  220. if nm.CollectServices != v {
  221. t.Errorf("netmap.CollectServices = %v; want %v", nm.CollectServices, v)
  222. }
  223. }
  224. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  225. Node: new(tailcfg.Node),
  226. })
  227. wantCollect(false)
  228. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  229. Node: new(tailcfg.Node),
  230. CollectServices: "false",
  231. })
  232. wantCollect(false)
  233. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  234. Node: new(tailcfg.Node),
  235. CollectServices: "true",
  236. })
  237. wantCollect(true)
  238. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  239. Node: new(tailcfg.Node),
  240. CollectServices: "",
  241. })
  242. wantCollect(true)
  243. })
  244. t.Run("implicit_domain", func(t *testing.T) {
  245. ms := newTestMapSession(t)
  246. var nm *netmap.NetworkMap
  247. want := func(v string) {
  248. t.Helper()
  249. if nm.Domain != v {
  250. t.Errorf("netmap.Domain = %q; want %q", nm.Domain, v)
  251. }
  252. }
  253. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  254. Node: new(tailcfg.Node),
  255. Domain: "foo.com",
  256. })
  257. want("foo.com")
  258. nm = ms.netmapForResponse(&tailcfg.MapResponse{
  259. Node: new(tailcfg.Node),
  260. })
  261. want("foo.com")
  262. })
  263. t.Run("implicit_node", func(t *testing.T) {
  264. someNode := &tailcfg.Node{
  265. Name: "foo",
  266. }
  267. wantNode := &tailcfg.Node{
  268. Name: "foo",
  269. ComputedName: "foo",
  270. ComputedNameWithHost: "foo",
  271. }
  272. ms := newTestMapSession(t)
  273. nm1 := ms.netmapForResponse(&tailcfg.MapResponse{
  274. Node: someNode,
  275. })
  276. if nm1.SelfNode == nil {
  277. t.Fatal("nil Node in 1st netmap")
  278. }
  279. if !reflect.DeepEqual(nm1.SelfNode, wantNode) {
  280. j, _ := json.Marshal(nm1.SelfNode)
  281. t.Errorf("Node mismatch in 1st netmap; got: %s", j)
  282. }
  283. nm2 := ms.netmapForResponse(&tailcfg.MapResponse{})
  284. if nm2.SelfNode == nil {
  285. t.Fatal("nil Node in 1st netmap")
  286. }
  287. if !reflect.DeepEqual(nm2.SelfNode, wantNode) {
  288. j, _ := json.Marshal(nm2.SelfNode)
  289. t.Errorf("Node mismatch in 2nd netmap; got: %s", j)
  290. }
  291. })
  292. }