cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2020.2.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

connection/discovery_test.go

317lines · modecode

1package connection
2
3import (
4 "net"
5 "sync"
6 "testing"
7 "testing/quick"
8 "time"
9
10 "github.com/sirupsen/logrus"
11 "github.com/stretchr/testify/assert"
12)
13
14func TestEdgeDiscovery(t *testing.T) {
15 mockAddrs := newMockAddrs(19, 2, 5)
16 netLookupSRV = mockNetLookupSRV(mockAddrs)
17 netLookupIP = mockNetLookupIP(mockAddrs)
18
19 expectedAddrSet := map[string]bool{}
20 for _, addrs := range mockAddrs.addrMap {
21 for _, addr := range addrs {
22 expectedAddrSet[addr.String()] = true
23 }
24 }
25
26 addrLists, err := EdgeDiscovery(logrus.New().WithFields(logrus.Fields{}))
27 assert.NoError(t, err)
28 actualAddrSet := map[string]bool{}
29 for _, addrs := range addrLists {
30 for _, addr := range addrs {
31 actualAddrSet[addr.String()] = true
32 }
33 }
34
35 assert.Equal(t, expectedAddrSet, actualAddrSet)
36}
37
38func TestAllInUse(t *testing.T) {
39 for _, testCase := range []struct {
40 regions []*region
41 expected map[string]*net.TCPAddr
42 }{
43 {
44 regions: nil,
45 expected: map[string]*net.TCPAddr{},
46 },
47 {
48 regions: []*region{
49 &region{inUse: map[string]*net.TCPAddr{}},
50 &region{inUse: map[string]*net.TCPAddr{}},
51 },
52 expected: map[string]*net.TCPAddr{},
53 },
54 {
55 regions: []*region{
56 &region{inUse: map[string]*net.TCPAddr{":1": &net.TCPAddr{Port: 1}}},
57 &region{inUse: map[string]*net.TCPAddr{":4": &net.TCPAddr{Port: 4}}},
58 },
59 expected: map[string]*net.TCPAddr{":1": &net.TCPAddr{Port: 1}, ":4": &net.TCPAddr{Port: 4}},
60 },
61 } {
62 actual := allInUse(testCase.regions)
63 assert.Equal(t, testCase.expected, actual)
64 }
65}
66
67func TestMakeRegions(t *testing.T) {
68 for _, testCase := range []struct {
69 addrList [][]*net.TCPAddr
70 inUse map[string]*net.TCPAddr
71 expected []*region
72 }{
73 {
74 addrList: [][]*net.TCPAddr{},
75 expected: nil,
76 },
77 {
78 addrList: [][]*net.TCPAddr{
79 []*net.TCPAddr{&net.TCPAddr{Port: 1}, &net.TCPAddr{Port: 2}},
80 },
81 expected: []*region{
82 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 1}, &net.TCPAddr{Port: 2}}, inUse: map[string]*net.TCPAddr{}},
83 },
84 },
85 {
86 addrList: [][]*net.TCPAddr{
87 []*net.TCPAddr{&net.TCPAddr{Port: 1}, &net.TCPAddr{Port: 2}},
88 []*net.TCPAddr{&net.TCPAddr{Port: 3}, &net.TCPAddr{Port: 4}},
89 },
90 expected: []*region{
91 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 1}, &net.TCPAddr{Port: 2}}, inUse: map[string]*net.TCPAddr{}},
92 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 3}, &net.TCPAddr{Port: 4}}, inUse: map[string]*net.TCPAddr{}},
93 },
94 },
95 {
96 addrList: [][]*net.TCPAddr{
97 []*net.TCPAddr{&net.TCPAddr{Port: 1}, &net.TCPAddr{Port: 2}},
98 []*net.TCPAddr{&net.TCPAddr{Port: 3}, &net.TCPAddr{Port: 4}},
99 },
100 inUse: map[string]*net.TCPAddr{
101 ":1": &net.TCPAddr{Port: 1},
102 ":4": &net.TCPAddr{Port: 4},
103 },
104 expected: []*region{
105 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 2}}, inUse: map[string]*net.TCPAddr{":1": &net.TCPAddr{Port: 1}}},
106 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 3}}, inUse: map[string]*net.TCPAddr{":4": &net.TCPAddr{Port: 4}}},
107 },
108 },
109 } {
110 actual := makeHARegions(testCase.addrList, testCase.inUse)
111 assert.Equal(t, testCase.expected, actual)
112 }
113}
114
115func assertIsBalanced(t *testing.T, regions []*region) bool {
116 // Compute max(len(region.addrs) for region in regions)
117 // No region should have significantly fewer addresses than this
118 var longestAddrs int
119 {
120 longestAddrs = 0
121 for _, region := range regions {
122 if l := len(region.addrs); l > longestAddrs {
123 longestAddrs = l
124 }
125 }
126 }
127 for _, region := range regions {
128 if len(region.addrs) == longestAddrs || len(region.addrs) == longestAddrs-1 {
129 continue
130 }
131 return assert.Fail(t,
132 "found a region with %v free addrs, while the longest addrs list is %v",
133 len(region.addrs), longestAddrs)
134 }
135 return true
136}
137
138// Various end-to-end tests, run with quickcheck (i.e. the testing/quick package)
139func TestEdgeAddrResolver(t *testing.T) {
140 concurrentReplacement := func(mockAddrs mockAddrs) bool {
141 netLookupSRV = mockNetLookupSRV(mockAddrs)
142 netLookupIP = mockNetLookupIP(mockAddrs)
143
144 resolver, err := NewEdgeAddrResolver(logrus.New())
145 if !assert.NoError(t, err) {
146 return false
147 }
148 assert.Equal(t, mockAddrs.numAddrs, resolver.AvailableAddrs(),
149 "every address should be initially available")
150
151 // Create several goroutines to simulate HA connections that acquire
152 // and replace IP addresses.
153 var wg sync.WaitGroup
154 wg.Add(mockAddrs.numAddrs)
155 for i := 0; i < mockAddrs.numAddrs; i++ {
156 go func() {
157 defer wg.Done()
158 const reconnectionCount = 50
159 for i := 0; i < reconnectionCount; i++ {
160 if resolver.AvailableAddrs() == 0 {
161 err = resolver.Refresh()
162 assert.NoError(t, err)
163 }
164 addr, err := resolver.Addr()
165 if !assert.NoError(t, err) {
166 return
167 }
168 time.Sleep(0) // allow some other goroutine to run
169 resolver.ReplaceAddr(addr)
170 time.Sleep(0) // allow some other goroutine to run
171 }
172 }()
173 }
174 wg.Wait()
175 assert.Equal(t, mockAddrs.numAddrs, resolver.AvailableAddrs(),
176 "every address should be available after replacement")
177 return !t.Failed()
178 }
179
180 badAddrWithRefresh := func(mockAddrs mockAddrs) bool {
181 netLookupSRV = mockNetLookupSRV(mockAddrs)
182 netLookupIP = mockNetLookupIP(mockAddrs)
183
184 resolver, err := NewEdgeAddrResolver(logrus.New())
185 if !assert.NoError(t, err) {
186 return false
187 }
188 assert.Equal(t, mockAddrs.numAddrs, resolver.AvailableAddrs(),
189 "every address should be initially available")
190
191 var addrs []*net.TCPAddr
192 for i := 0; i < mockAddrs.numAddrs; i++ {
193 assert.Equal(t, mockAddrs.numAddrs-i, resolver.AvailableAddrs())
194 addr, err := resolver.Addr()
195 assert.NoError(t, err)
196 addrs = append(addrs, addr)
197 }
198 assert.Equal(t, 0, resolver.AvailableAddrs(), "all addresses should have been taken")
199 _, err = resolver.Addr()
200 assert.Error(t, err)
201
202 anyAddr, err := resolver.AnyAddr()
203 assert.NoError(t, err, "should still be okay to call AnyAddr")
204
205 resolver.MarkAddrBad(anyAddr)
206
207 assert.Equal(t, 0, resolver.AvailableAddrs(), "all addresses should still be used")
208 _, err = resolver.Addr()
209 assert.Error(t, err, "all addresses should still be used")
210
211 err = resolver.Refresh()
212 assert.NoError(t, err, "Refresh() should have worked")
213
214 assert.Equal(t, 1, resolver.AvailableAddrs(),
215 "Refresh() should have reset the state of the 'bad' address")
216 addr, err := resolver.Addr()
217 assert.NoError(t, err)
218 assert.Equal(t, anyAddr, addr)
219
220 _, err = resolver.Addr()
221 assert.Error(t, err, "all addresses should be used again")
222
223 return !t.Failed()
224 }
225
226 assert.NoError(t, quick.Check(concurrentReplacement, nil))
227 assert.NoError(t, quick.Check(badAddrWithRefresh, nil))
228}
229
230// "White-box" test: runs Addr() and checks internal state
231func TestEdgeAddrResolver_Addr(t *testing.T) {
232 e := &EdgeAddrResolver{regions: nil}
233 addr, err := e.Addr()
234 assert.Error(t, err)
235
236 testRegions := func() []*region {
237 return []*region{
238 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 1}}, inUse: map[string]*net.TCPAddr{":2": &net.TCPAddr{Port: 2}, ":3": &net.TCPAddr{Port: 3}}},
239 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 4}, &net.TCPAddr{Port: 5}}, inUse: map[string]*net.TCPAddr{":6": &net.TCPAddr{Port: 6}}},
240 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 7}, &net.TCPAddr{Port: 8}}, inUse: map[string]*net.TCPAddr{":9": &net.TCPAddr{Port: 9}}},
241 }
242 }
243 e = &EdgeAddrResolver{regions: testRegions()}
244 addr, err = e.Addr()
245 assert.NoError(t, err)
246 assert.Equal(t, &net.TCPAddr{Port: 4}, addr)
247 var expected []*region
248 {
249 expected = testRegions()
250 expected[1].addrs = expected[1].addrs[1:]
251 expected[1].inUse[":4"] = &net.TCPAddr{Port: 4}
252 }
253 assert.Equal(t, expected, e.regions)
254}
255
256// "White-box" test: runs AnyAddr() and checks internal state
257func TestEdgeAddrResolver_AnyAddr(t *testing.T) {
258 e := &EdgeAddrResolver{regions: nil}
259 addr, err := e.AnyAddr()
260 assert.Error(t, err)
261
262 e = &EdgeAddrResolver{regions: []*region{&region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 1}}, inUse: map[string]*net.TCPAddr{":2": &net.TCPAddr{Port: 2}}}}}
263 addr, err = e.AnyAddr()
264 assert.NoError(t, err)
265 assert.Equal(t, &net.TCPAddr{Port: 1}, addr, "should have chosen the inactive address")
266
267 e = &EdgeAddrResolver{regions: []*region{&region{inUse: map[string]*net.TCPAddr{":1": &net.TCPAddr{Port: 1}}}}}
268 addr, err = e.AnyAddr()
269 assert.NoError(t, err)
270 assert.Equal(t, &net.TCPAddr{Port: 1}, addr, "should have chosen an active address rather than nothing")
271}
272
273// "White-box" test: runs ReplaceAddr() and checks internal state
274func TestEdgeAddrResolver_ReplaceAddr(t *testing.T) {
275 e := &EdgeAddrResolver{regions: nil}
276 e.ReplaceAddr(&net.TCPAddr{Port: 1}) // this shouldn't panic, I guess
277
278 testRegions := func() []*region {
279 return []*region{
280 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 1}}, inUse: map[string]*net.TCPAddr{":2": &net.TCPAddr{Port: 2}, ":3": &net.TCPAddr{Port: 3}}},
281 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 4}, &net.TCPAddr{Port: 5}}, inUse: map[string]*net.TCPAddr{":6": &net.TCPAddr{Port: 6}}},
282 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 7}, &net.TCPAddr{Port: 8}}, inUse: map[string]*net.TCPAddr{":9": &net.TCPAddr{Port: 9}}},
283 }
284 }
285 e = &EdgeAddrResolver{regions: testRegions()}
286 e.ReplaceAddr(&net.TCPAddr{Port: 6})
287 var expected []*region
288 {
289 expected = testRegions()
290 delete(expected[1].inUse, ":6")
291 expected[1].addrs = append(expected[1].addrs, &net.TCPAddr{Port: 6})
292 }
293 assert.Equal(t, expected, e.regions)
294}
295
296// "White-box" test: runs MarkAddrBad() and checks internal state
297func TestEdgeAddrResolver_MarkAddrBad(t *testing.T) {
298 e := &EdgeAddrResolver{regions: nil}
299 e.ReplaceAddr(&net.TCPAddr{Port: 1}) // this shouldn't panic, I guess
300
301 testRegions := func() []*region {
302 return []*region{
303 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 1}}, inUse: map[string]*net.TCPAddr{":2": &net.TCPAddr{Port: 2}, ":3": &net.TCPAddr{Port: 3}}},
304 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 4}, &net.TCPAddr{Port: 5}}, inUse: map[string]*net.TCPAddr{":6": &net.TCPAddr{Port: 6}}},
305 &region{addrs: []*net.TCPAddr{&net.TCPAddr{Port: 7}, &net.TCPAddr{Port: 8}}, inUse: map[string]*net.TCPAddr{":9": &net.TCPAddr{Port: 9}}},
306 }
307 }
308 e = &EdgeAddrResolver{regions: testRegions()}
309 e.MarkAddrBad(&net.TCPAddr{Port: 6})
310 var expected []*region
311 {
312 expected = testRegions()
313 delete(expected[1].inUse, ":6")
314 expected[1].bad = append(expected[1].bad, &net.TCPAddr{Port: 6})
315 }
316 assert.Equal(t, expected, e.regions)
317}
318