Skip to content

Commit c7a6304

Browse files
committed
TUN-6007: Implement new edge discovery algorithm
(cherry picked from commit 4f468b8)
1 parent f4667c6 commit c7a6304

File tree

14 files changed

+1333
-542
lines changed

14 files changed

+1333
-542
lines changed

connection/errors.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ func (e DupConnRegisterTunnelError) Error() string {
1818
return "already connected to this server, trying another address"
1919
}
2020

21+
// Dial to edge server with quic failed
22+
type EdgeQuicDialError struct {
23+
Cause error
24+
}
25+
26+
func (e *EdgeQuicDialError) Error() string {
27+
return "failed to dial to edge with quic: " + e.Cause.Error()
28+
}
29+
2130
// RegisterTunnel error from server
2231
type ServerRegisterTunnelError struct {
2332
Cause error

connection/quic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ func NewQUICConnection(
5757
) (*QUICConnection, error) {
5858
session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig)
5959
if err != nil {
60-
return nil, fmt.Errorf("failed to dial to edge: %w", err)
60+
return nil, &EdgeQuicDialError{Cause: err}
6161
}
6262

6363
datagramMuxer, err := quicpogs.NewDatagramMuxer(session, logger)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package allregions
2+
3+
// Region contains cloudflared edge addresses. The edge is partitioned into several regions for
4+
// redundancy purposes.
5+
type AddrSet map[*EdgeAddr]UsedBy
6+
7+
// AddrUsedBy finds the address used by the given connection in this region.
8+
// Returns nil if the connection isn't using any IP.
9+
func (a AddrSet) AddrUsedBy(connID int) *EdgeAddr {
10+
for addr, used := range a {
11+
if used.Used && used.ConnID == connID {
12+
return addr
13+
}
14+
}
15+
return nil
16+
}
17+
18+
// AvailableAddrs counts how many unused addresses this region contains.
19+
func (a AddrSet) AvailableAddrs() int {
20+
n := 0
21+
for _, usedby := range a {
22+
if !usedby.Used {
23+
n++
24+
}
25+
}
26+
return n
27+
}
28+
29+
// GetUnusedIP returns a random unused address in this region.
30+
// Returns nil if all addresses are in use.
31+
func (a AddrSet) GetUnusedIP(excluding *EdgeAddr) *EdgeAddr {
32+
for addr, usedby := range a {
33+
if !usedby.Used && addr != excluding {
34+
return addr
35+
}
36+
}
37+
return nil
38+
}
39+
40+
// Use the address, assigning it to a proxy connection.
41+
func (a AddrSet) Use(addr *EdgeAddr, connID int) {
42+
if addr == nil {
43+
return
44+
}
45+
a[addr] = InUse(connID)
46+
}
47+
48+
// GetAnyAddress returns an arbitrary address from the region.
49+
func (a AddrSet) GetAnyAddress() *EdgeAddr {
50+
for addr := range a {
51+
return addr
52+
}
53+
return nil
54+
}
55+
56+
// GiveBack the address, ensuring it is no longer assigned to an IP.
57+
// Returns true if the address is in this region.
58+
func (a AddrSet) GiveBack(addr *EdgeAddr) (ok bool) {
59+
if _, ok := a[addr]; !ok {
60+
return false
61+
}
62+
a[addr] = Unused()
63+
return true
64+
}
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
package allregions
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestAddrSet_AddrUsedBy(t *testing.T) {
9+
type args struct {
10+
connID int
11+
}
12+
tests := []struct {
13+
name string
14+
addrSet AddrSet
15+
args args
16+
want *EdgeAddr
17+
}{
18+
{
19+
name: "happy trivial test",
20+
addrSet: AddrSet{
21+
&addr0: InUse(0),
22+
},
23+
args: args{connID: 0},
24+
want: &addr0,
25+
},
26+
{
27+
name: "sad trivial test",
28+
addrSet: AddrSet{
29+
&addr0: InUse(0),
30+
},
31+
args: args{connID: 1},
32+
want: nil,
33+
},
34+
{
35+
name: "sad test",
36+
addrSet: AddrSet{
37+
&addr0: InUse(0),
38+
&addr1: InUse(1),
39+
&addr2: InUse(2),
40+
},
41+
args: args{connID: 3},
42+
want: nil,
43+
},
44+
{
45+
name: "happy test",
46+
addrSet: AddrSet{
47+
&addr0: InUse(0),
48+
&addr1: InUse(1),
49+
&addr2: InUse(2),
50+
},
51+
args: args{connID: 1},
52+
want: &addr1,
53+
},
54+
}
55+
for _, tt := range tests {
56+
t.Run(tt.name, func(t *testing.T) {
57+
if got := tt.addrSet.AddrUsedBy(tt.args.connID); !reflect.DeepEqual(got, tt.want) {
58+
t.Errorf("Region.AddrUsedBy() = %v, want %v", got, tt.want)
59+
}
60+
})
61+
}
62+
}
63+
64+
func TestAddrSet_AvailableAddrs(t *testing.T) {
65+
tests := []struct {
66+
name string
67+
addrSet AddrSet
68+
want int
69+
}{
70+
{
71+
name: "contains addresses",
72+
addrSet: AddrSet{
73+
&addr0: InUse(0),
74+
&addr1: Unused(),
75+
&addr2: InUse(2),
76+
},
77+
want: 1,
78+
},
79+
{
80+
name: "all free",
81+
addrSet: AddrSet{
82+
&addr0: Unused(),
83+
&addr1: Unused(),
84+
&addr2: Unused(),
85+
},
86+
want: 3,
87+
},
88+
{
89+
name: "all used",
90+
addrSet: AddrSet{
91+
&addr0: InUse(0),
92+
&addr1: InUse(1),
93+
&addr2: InUse(2),
94+
},
95+
want: 0,
96+
},
97+
{
98+
name: "empty",
99+
addrSet: AddrSet{},
100+
want: 0,
101+
},
102+
}
103+
for _, tt := range tests {
104+
t.Run(tt.name, func(t *testing.T) {
105+
if got := tt.addrSet.AvailableAddrs(); got != tt.want {
106+
t.Errorf("Region.AvailableAddrs() = %v, want %v", got, tt.want)
107+
}
108+
})
109+
}
110+
}
111+
112+
func TestAddrSet_GetUnusedIP(t *testing.T) {
113+
type args struct {
114+
excluding *EdgeAddr
115+
}
116+
tests := []struct {
117+
name string
118+
addrSet AddrSet
119+
args args
120+
want *EdgeAddr
121+
}{
122+
{
123+
name: "happy test with excluding set",
124+
addrSet: AddrSet{
125+
&addr0: Unused(),
126+
&addr1: Unused(),
127+
&addr2: InUse(2),
128+
},
129+
args: args{excluding: &addr0},
130+
want: &addr1,
131+
},
132+
{
133+
name: "happy test with no excluding",
134+
addrSet: AddrSet{
135+
&addr0: InUse(0),
136+
&addr1: Unused(),
137+
&addr2: InUse(2),
138+
},
139+
args: args{excluding: nil},
140+
want: &addr1,
141+
},
142+
{
143+
name: "sad test with no excluding",
144+
addrSet: AddrSet{
145+
&addr0: InUse(0),
146+
&addr1: InUse(1),
147+
&addr2: InUse(2),
148+
},
149+
args: args{excluding: nil},
150+
want: nil,
151+
},
152+
{
153+
name: "sad test with excluding",
154+
addrSet: AddrSet{
155+
&addr0: Unused(),
156+
&addr1: InUse(1),
157+
&addr2: InUse(2),
158+
},
159+
args: args{excluding: &addr0},
160+
want: nil,
161+
},
162+
}
163+
for _, tt := range tests {
164+
t.Run(tt.name, func(t *testing.T) {
165+
if got := tt.addrSet.GetUnusedIP(tt.args.excluding); !reflect.DeepEqual(got, tt.want) {
166+
t.Errorf("Region.GetUnusedIP() = %v, want %v", got, tt.want)
167+
}
168+
})
169+
}
170+
}
171+
172+
func TestAddrSet_GiveBack(t *testing.T) {
173+
type args struct {
174+
addr *EdgeAddr
175+
}
176+
tests := []struct {
177+
name string
178+
addrSet AddrSet
179+
args args
180+
wantOk bool
181+
availableAfter int
182+
}{
183+
{
184+
name: "sad test with excluding",
185+
addrSet: AddrSet{
186+
&addr1: InUse(1),
187+
},
188+
args: args{addr: &addr1},
189+
wantOk: true,
190+
availableAfter: 1,
191+
},
192+
{
193+
name: "sad test with excluding",
194+
addrSet: AddrSet{
195+
&addr1: InUse(1),
196+
},
197+
args: args{addr: &addr2},
198+
wantOk: false,
199+
availableAfter: 0,
200+
},
201+
}
202+
for _, tt := range tests {
203+
t.Run(tt.name, func(t *testing.T) {
204+
if gotOk := tt.addrSet.GiveBack(tt.args.addr); gotOk != tt.wantOk {
205+
t.Errorf("Region.GiveBack() = %v, want %v", gotOk, tt.wantOk)
206+
}
207+
if tt.availableAfter != tt.addrSet.AvailableAddrs() {
208+
t.Errorf("Region.AvailableAddrs() = %v, want %v", tt.addrSet.AvailableAddrs(), tt.availableAfter)
209+
}
210+
})
211+
}
212+
}
213+
214+
func TestAddrSet_GetAnyAddress(t *testing.T) {
215+
tests := []struct {
216+
name string
217+
addrSet AddrSet
218+
wantNil bool
219+
}{
220+
{
221+
name: "Sad test -- GetAnyAddress should only fail if the region is empty",
222+
addrSet: AddrSet{},
223+
wantNil: true,
224+
},
225+
{
226+
name: "Happy test (all addresses unused)",
227+
addrSet: AddrSet{
228+
&addr0: Unused(),
229+
},
230+
wantNil: false,
231+
},
232+
{
233+
name: "Happy test (GetAnyAddress can still return addresses used by proxy conns)",
234+
addrSet: AddrSet{
235+
&addr0: InUse(2),
236+
},
237+
wantNil: false,
238+
},
239+
}
240+
for _, tt := range tests {
241+
t.Run(tt.name, func(t *testing.T) {
242+
if got := tt.addrSet.GetAnyAddress(); tt.wantNil != (got == nil) {
243+
t.Errorf("Region.GetAnyAddress() = %v, but should it return nil? %v", got, tt.wantNil)
244+
}
245+
})
246+
}
247+
}

edgediscovery/allregions/discovery.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313

1414
const (
1515
// Used to discover HA origintunneld servers
16-
srvService = "origintunneld"
16+
srvService = "v2-origintunneld"
1717
srvProto = "tcp"
1818
srvName = "argotunnel.com"
1919

@@ -115,6 +115,9 @@ func edgeDiscovery(log *zerolog.Logger, srvService string) ([][]*EdgeAddr, error
115115
if err != nil {
116116
return nil, err
117117
}
118+
for _, e := range edgeAddrs {
119+
log.Debug().Msgf("Edge Address: %+v", *e)
120+
}
118121
resolvedAddrPerCNAME = append(resolvedAddrPerCNAME, edgeAddrs)
119122
}
120123

@@ -187,7 +190,6 @@ func ResolveAddrs(addrs []string, log *zerolog.Logger) (resolved []*EdgeAddr) {
187190
UDP: udpAddr,
188191
IPVersion: version,
189192
})
190-
191193
}
192194
return
193195
}

0 commit comments

Comments
 (0)