@@ -10,7 +10,6 @@ import (
10
10
"testing"
11
11
"time"
12
12
13
- "github.com/k3s-io/k3s/pkg/cli/cmds"
14
13
"github.com/sirupsen/logrus"
15
14
)
16
15
@@ -24,7 +23,7 @@ type testServer struct {
24
23
prefix string
25
24
}
26
25
27
- func createServer (prefix string ) (* testServer , error ) {
26
+ func createServer (ctx context. Context , prefix string ) (* testServer , error ) {
28
27
listener , err := net .Listen ("tcp" , "127.0.0.1:0" )
29
28
if err != nil {
30
29
return nil , err
@@ -34,6 +33,10 @@ func createServer(prefix string) (*testServer, error) {
34
33
listener : listener ,
35
34
}
36
35
go s .serve ()
36
+ go func () {
37
+ <- ctx .Done ()
38
+ s .close ()
39
+ }()
37
40
return s , nil
38
41
}
39
42
@@ -49,6 +52,7 @@ func (s *testServer) serve() {
49
52
}
50
53
51
54
func (s * testServer ) close () {
55
+ logrus .Printf ("testServer %s closing" , s .prefix )
52
56
s .listener .Close ()
53
57
for _ , conn := range s .conns {
54
58
conn .Close ()
@@ -65,6 +69,10 @@ func (s *testServer) echo(conn net.Conn) {
65
69
}
66
70
}
67
71
72
+ func (s * testServer ) address () string {
73
+ return s .listener .Addr ().String ()
74
+ }
75
+
68
76
func ping (conn net.Conn ) (string , error ) {
69
77
fmt .Fprintf (conn , "ping\n " )
70
78
result , err := bufio .NewReader (conn ).ReadString ('\n' )
@@ -74,25 +82,31 @@ func ping(conn net.Conn) (string, error) {
74
82
return strings .TrimSpace (result ), nil
75
83
}
76
84
85
+ // Test_UnitFailOver creates a LB using a default server (ie fixed registration endpoint)
86
+ // and then adds a new server (a node). The node server is then closed, and it is confirmed
87
+ // that new connections use the default server.
77
88
func Test_UnitFailOver (t * testing.T ) {
78
89
tmpDir := t .TempDir ()
90
+ ctx , cancel := context .WithCancel (context .Background ())
91
+ defer cancel ()
79
92
80
- ogServe , err := createServer ("og " )
93
+ defaultServer , err := createServer (ctx , "default " )
81
94
if err != nil {
82
- t .Fatalf ("createServer(og ) failed: %v" , err )
95
+ t .Fatalf ("createServer(default ) failed: %v" , err )
83
96
}
84
97
85
- lbServe , err := createServer ("lb " )
98
+ node1Server , err := createServer (ctx , "node1 " )
86
99
if err != nil {
87
- t .Fatalf ("createServer(lb ) failed: %v" , err )
100
+ t .Fatalf ("createServer(node1 ) failed: %v" , err )
88
101
}
89
102
90
- cfg := cmds. Agent {
91
- ServerURL : fmt . Sprintf ( "http://%s/" , ogServe . listener . Addr (). String ()),
92
- DataDir : tmpDir ,
103
+ node2Server , err := createServer ( ctx , "node2" )
104
+ if err != nil {
105
+ t . Fatalf ( "createServer(node2) failed: %v" , err )
93
106
}
94
107
95
- lb , err := New (context .TODO (), cfg .DataDir , SupervisorServiceName , cfg .ServerURL , RandomPort , false )
108
+ // start the loadbalancer with the default server as the only server
109
+ lb , err := New (ctx , tmpDir , SupervisorServiceName , "http://" + defaultServer .address (), RandomPort , false )
96
110
if err != nil {
97
111
t .Fatalf ("New() failed: %v" , err )
98
112
}
@@ -103,50 +117,123 @@ func Test_UnitFailOver(t *testing.T) {
103
117
}
104
118
localAddress := parsedURL .Host
105
119
106
- lb .Update ([]string {lbServe .listener .Addr ().String ()})
120
+ // add the node as a new server address.
121
+ lb .Update ([]string {node1Server .address ()})
107
122
123
+ // make sure connections go to the node
108
124
conn1 , err := net .Dial ("tcp" , localAddress )
109
125
if err != nil {
110
126
t .Fatalf ("net.Dial failed: %v" , err )
111
127
}
112
- result1 , err := ping (conn1 )
113
- if err != nil {
128
+ if result , err := ping (conn1 ); err != nil {
114
129
t .Fatalf ("ping(conn1) failed: %v" , err )
130
+ } else if result != "node1:ping" {
131
+ t .Fatalf ("Unexpected ping(conn1) result: %v" , result )
115
132
}
116
- if result1 != "lb:ping" {
117
- t .Fatalf ("Unexpected ping result: %v" , result1 )
118
- }
119
133
120
- lbServe .close ()
134
+ t .Log ("conn1 tested OK" )
135
+
136
+ // set failing health check for node 1
137
+ lb .SetHealthCheck (node1Server .address (), func () bool { return false })
138
+
139
+ // Server connections are checked every second, now that node 1 is failed
140
+ // the connections to it should be closed.
141
+ time .Sleep (2 * time .Second )
121
142
122
- _ , err = ping (conn1 )
123
- if err == nil {
143
+ if _ , err := ping (conn1 ); err == nil {
124
144
t .Fatal ("Unexpected successful ping on closed connection conn1" )
125
145
}
126
146
147
+ t .Log ("conn1 closed on failure OK" )
148
+
149
+ // make sure connection still goes to the first node - it is failing health checks but so
150
+ // is the default endpoint, so it should be tried first with health checks disabled,
151
+ // before failing back to the default.
127
152
conn2 , err := net .Dial ("tcp" , localAddress )
128
153
if err != nil {
129
154
t .Fatalf ("net.Dial failed: %v" , err )
130
155
131
156
}
132
- result2 , err := ping (conn2 )
133
- if err != nil {
157
+ if result , err := ping (conn2 ); err != nil {
134
158
t .Fatalf ("ping(conn2) failed: %v" , err )
159
+ } else if result != "node1:ping" {
160
+ t .Fatalf ("Unexpected ping(conn2) result: %v" , result )
135
161
}
136
- if result2 != "og:ping" {
137
- t .Fatalf ("Unexpected ping result: %v" , result2 )
162
+
163
+ t .Log ("conn2 tested OK" )
164
+
165
+ // make sure the health checks don't close the connection we just made -
166
+ // connections should only be closed when it transitions from health to unhealthy.
167
+ time .Sleep (2 * time .Second )
168
+
169
+ if result , err := ping (conn2 ); err != nil {
170
+ t .Fatalf ("ping(conn2) failed: %v" , err )
171
+ } else if result != "node1:ping" {
172
+ t .Fatalf ("Unexpected ping(conn2) result: %v" , result )
138
173
}
174
+
175
+ t .Log ("conn2 tested OK again" )
176
+
177
+ // shut down the first node server to force failover to the default
178
+ node1Server .close ()
179
+
180
+ // make sure new connections go to the default, and existing connections are closed
181
+ conn3 , err := net .Dial ("tcp" , localAddress )
182
+ if err != nil {
183
+ t .Fatalf ("net.Dial failed: %v" , err )
184
+
185
+ }
186
+ if result , err := ping (conn3 ); err != nil {
187
+ t .Fatalf ("ping(conn3) failed: %v" , err )
188
+ } else if result != "default:ping" {
189
+ t .Fatalf ("Unexpected ping(conn3) result: %v" , result )
190
+ }
191
+
192
+ t .Log ("conn3 tested OK" )
193
+
194
+ if _ , err := ping (conn2 ); err == nil {
195
+ t .Fatal ("Unexpected successful ping on closed connection conn2" )
196
+ }
197
+
198
+ t .Log ("conn2 closed on failure OK" )
199
+
200
+ // add the second node as a new server address.
201
+ lb .Update ([]string {node2Server .address ()})
202
+
203
+ // make sure connection now goes to the second node,
204
+ // and connections to the default are closed.
205
+ conn4 , err := net .Dial ("tcp" , localAddress )
206
+ if err != nil {
207
+ t .Fatalf ("net.Dial failed: %v" , err )
208
+
209
+ }
210
+ if result , err := ping (conn4 ); err != nil {
211
+ t .Fatalf ("ping(conn4) failed: %v" , err )
212
+ } else if result != "node2:ping" {
213
+ t .Fatalf ("Unexpected ping(conn4) result: %v" , result )
214
+ }
215
+
216
+ t .Log ("conn4 tested OK" )
217
+
218
+ // Server connections are checked every second, now that we have a healthy
219
+ // server, connections to the default server should be closed
220
+ time .Sleep (2 * time .Second )
221
+
222
+ if _ , err := ping (conn3 ); err == nil {
223
+ t .Fatal ("Unexpected successful ping on connection conn3" )
224
+ }
225
+
226
+ t .Log ("conn3 closed on failure OK" )
139
227
}
140
228
229
+ // Test_UnitFailFast confirms that connnections to invalid addresses fail quickly
141
230
func Test_UnitFailFast (t * testing.T ) {
142
231
tmpDir := t .TempDir ()
232
+ ctx , cancel := context .WithCancel (context .Background ())
233
+ defer cancel ()
143
234
144
- cfg := cmds.Agent {
145
- ServerURL : "http://127.0.0.1:0/" ,
146
- DataDir : tmpDir ,
147
- }
148
-
149
- lb , err := New (context .TODO (), cfg .DataDir , SupervisorServiceName , cfg .ServerURL , RandomPort , false )
235
+ serverURL := "http://127.0.0.1:0/"
236
+ lb , err := New (ctx , tmpDir , SupervisorServiceName , serverURL , RandomPort , false )
150
237
if err != nil {
151
238
t .Fatalf ("New() failed: %v" , err )
152
239
}
@@ -172,3 +259,44 @@ func Test_UnitFailFast(t *testing.T) {
172
259
t .Fatal ("Test timed out" )
173
260
}
174
261
}
262
+
263
+ // Test_UnitFailUnreachable confirms that connnections to unreachable addresses do fail
264
+ // within the expected duration
265
+ func Test_UnitFailUnreachable (t * testing.T ) {
266
+ if testing .Short () {
267
+ t .Skip ("skipping slow test in short mode." )
268
+ }
269
+ tmpDir := t .TempDir ()
270
+ ctx , cancel := context .WithCancel (context .Background ())
271
+ defer cancel ()
272
+
273
+ serverAddr := "192.0.2.1:6443"
274
+ lb , err := New (ctx , tmpDir , SupervisorServiceName , "http://" + serverAddr , RandomPort , false )
275
+ if err != nil {
276
+ t .Fatalf ("New() failed: %v" , err )
277
+ }
278
+
279
+ // Set failing health check to reduce retries
280
+ lb .SetHealthCheck (serverAddr , func () bool { return false })
281
+
282
+ conn , err := net .Dial ("tcp" , lb .localAddress )
283
+ if err != nil {
284
+ t .Fatalf ("net.Dial failed: %v" , err )
285
+ }
286
+
287
+ done := make (chan error )
288
+ go func () {
289
+ _ , err = ping (conn )
290
+ done <- err
291
+ }()
292
+ timeout := time .After (11 * time .Second )
293
+
294
+ select {
295
+ case err := <- done :
296
+ if err == nil {
297
+ t .Fatal ("Unexpected successful ping from unreachable address" )
298
+ }
299
+ case <- timeout :
300
+ t .Fatal ("Test timed out" )
301
+ }
302
+ }
0 commit comments