@@ -20,9 +20,10 @@ limitations under the License.
20
20
package integration
21
21
22
22
import (
23
+ "bufio"
23
24
"context"
24
25
"fmt"
25
- "net/http "
26
+ "net"
26
27
"strings"
27
28
"testing"
28
29
"time"
@@ -38,9 +39,12 @@ import (
38
39
39
40
const (
40
41
tcprouteSampleKustomize = "../../config/tests/tcproute"
42
+ tcprouteRRKustomize = "../../config/tests/tcproute-rr"
41
43
tcprouteSampleName = "blixt-tcproute-sample"
42
44
)
43
45
46
+ var tcpServerNames = []string {"blixt-tcproute-sample" , "tcproute-rr-v1" , "tcproute-rr-v2" }
47
+
44
48
func TestTCPRouteBasics (t * testing.T ) {
45
49
tcpRouteBasicsCleanupKey := "tcproutebasics"
46
50
defer func () {
@@ -69,38 +73,184 @@ func TestTCPRouteBasics(t *testing.T) {
69
73
require .Equal (t , gatewayv1beta1 .IPAddressType , * gw .Status .Addresses [0 ].Type )
70
74
gwaddr := fmt .Sprintf ("%s:8080" , gw .Status .Addresses [0 ].Value )
71
75
72
- t .Log ("waiting for HTTP server to be available" )
76
+ t .Log ("waiting for TCP server to be available" )
73
77
require .Eventually (t , func () bool {
74
78
server , err := env .Cluster ().Client ().AppsV1 ().Deployments (corev1 .NamespaceDefault ).Get (ctx , tcprouteSampleName , metav1.GetOptions {})
75
79
require .NoError (t , err )
76
80
return server .Status .AvailableReplicas > 0
77
81
}, time .Minute , time .Second )
78
82
79
- t .Log ("verifying HTTP connectivity to the server" )
80
- httpc := http. Client { Timeout : time . Second * 30 }
83
+ t .Log ("verifying TCP connectivity to the server" )
84
+ var conn net. Conn
81
85
require .Eventually (t , func () bool {
82
- resp , err := httpc .Get (fmt .Sprintf ("http://%s/status/%d" , gwaddr , http .StatusTeapot ))
86
+ var err error
87
+ conn , err = net .Dial ("tcp" , gwaddr )
83
88
if err != nil {
84
- t .Logf ("received error checking HTTP server: [%s], retrying..." , err )
89
+ t .Logf ("received error connecting to TCP server: [%s], retrying..." , err )
85
90
return false
86
91
}
87
- defer resp .Body .Close ()
88
- return resp .StatusCode == http .StatusTeapot
92
+ return true
89
93
}, time .Minute * 5 , time .Second )
90
94
91
- t .Log ("deleting the TCPRoute and verifying that HTTP traffic stops" )
95
+ response := writeAndReadTCP (t , conn )
96
+ require .Contains (t , response , tcpServerNames [0 ])
97
+
98
+ t .Log ("deleting the TCPRoute and verifying that TCP connection is closed" )
92
99
require .NoError (t , gwclient .GatewayV1alpha2 ().TCPRoutes (corev1 .NamespaceDefault ).Delete (ctx , tcprouteSampleName , metav1.DeleteOptions {}))
93
- httpc = http.Client {Timeout : time .Second * 3 }
94
100
require .Eventually (t , func () bool {
95
- resp , err := httpc .Get (fmt .Sprintf ("http://%s/status/%d" , gwaddr , http .StatusTeapot ))
101
+ _ , err := conn .Write ([]byte ("blahhh\n " ))
102
+ require .NoError (t , err )
103
+
104
+ err = conn .SetReadDeadline (time .Now ().Add (time .Second * 3 ))
105
+ require .NoError (t , err )
106
+ reader := bufio .NewReader (conn )
107
+ _ , err = reader .ReadBytes (byte ('\n' ))
96
108
if err != nil {
97
- if strings .Contains (err .Error (), "context deadline exceeded " ) {
109
+ if strings .Contains (err .Error (), "i/o timeout " ) {
98
110
return true
99
111
}
100
112
t .Logf ("received unexpected error waiting for TCPRoute to decomission: %s" , err )
101
113
return false
102
114
}
103
- defer resp .Body .Close ()
104
115
return false
105
116
}, time .Minute , time .Second )
106
117
}
118
+
119
+ func TestTCPRouteRoundRobin (t * testing.T ) {
120
+ tcpRouteRRCleanupKey := "tcprouterr"
121
+ defer func () {
122
+ testutils .DumpDiagnosticsIfFailed (ctx , t , env .Cluster ())
123
+ if err := runCleanup (tcpRouteRRCleanupKey ); err != nil {
124
+ t .Errorf ("cleanup failed: %s" , err )
125
+ }
126
+ }()
127
+
128
+ t .Log ("deploying config/samples/tcproute-rr kustomize" )
129
+ require .NoError (t , clusters .KustomizeDeployForCluster (ctx , env .Cluster (), tcprouteRRKustomize ))
130
+ addCleanup (tcpRouteRRCleanupKey , func (ctx context.Context ) error {
131
+ cleanupLog ("cleaning up config/samples/tcproute-rr kustomize" )
132
+ return clusters .KustomizeDeleteForCluster (ctx , env .Cluster (), tcprouteRRKustomize , "--ignore-not-found=true" )
133
+ })
134
+
135
+ t .Log ("waiting for Gateway to have an address" )
136
+ var gw * gatewayv1beta1.Gateway
137
+ require .Eventually (t , func () bool {
138
+ var err error
139
+ gw , err = gwclient .GatewayV1beta1 ().Gateways (corev1 .NamespaceDefault ).Get (ctx , tcprouteSampleName , metav1.GetOptions {})
140
+ require .NoError (t , err )
141
+ return len (gw .Status .Addresses ) > 0
142
+ }, time .Minute , time .Second )
143
+ require .NotNil (t , gw .Status .Addresses [0 ].Type )
144
+ require .Equal (t , gatewayv1beta1 .IPAddressType , * gw .Status .Addresses [0 ].Type )
145
+ gwaddr := fmt .Sprintf ("%s:8080" , gw .Status .Addresses [0 ].Value )
146
+
147
+ t .Log ("waiting for TCP servers to be available" )
148
+ labelSelector := metav1.LabelSelector {
149
+ MatchExpressions : []metav1.LabelSelectorRequirement {
150
+ {
151
+ Key : "app" ,
152
+ Operator : metav1 .LabelSelectorOpIn ,
153
+ Values : tcpServerNames ,
154
+ },
155
+ },
156
+ }
157
+ require .Eventually (t , func () bool {
158
+ servers , err := env .Cluster ().Client ().AppsV1 ().Deployments (corev1 .NamespaceDefault ).List (ctx , metav1.ListOptions {
159
+ LabelSelector : metav1 .FormatLabelSelector (& labelSelector ),
160
+ })
161
+ require .NoError (t , err )
162
+ for _ , server := range servers .Items {
163
+ if server .Status .AvailableReplicas <= 0 {
164
+ return false
165
+ }
166
+ }
167
+ return true
168
+ }, time .Minute , time .Second )
169
+
170
+ t .Log ("verifying TCP connectivity to the servers" )
171
+ // We create three TCP connections, one for each backend.
172
+ var conn1 net.Conn
173
+ require .Eventually (t , func () bool {
174
+ var err error
175
+ conn1 , err = net .Dial ("tcp" , gwaddr )
176
+ if err != nil {
177
+ t .Logf ("received error connecting to TCP server: [%s], retrying..." , err )
178
+ return false
179
+ }
180
+ return true
181
+ }, time .Minute * 5 , time .Second )
182
+ conn2 , err := net .Dial ("tcp" , gwaddr )
183
+ require .NoError (t , err )
184
+ conn3 , err := net .Dial ("tcp" , gwaddr )
185
+ require .NoError (t , err )
186
+ conns := []net.Conn {conn1 , conn2 , conn3 }
187
+
188
+ // Run it twice to verify that we load balance in a round-robin fashion.
189
+ for c := 0 ; c < 2 ; c ++ {
190
+ // We can't do names := tcpServerNames because we overwrite this in the loop later.
191
+ var names []string
192
+ names = append (names , tcpServerNames ... )
193
+
194
+ for _ , conn := range conns {
195
+ response := writeAndReadTCP (t , conn )
196
+ split := strings .Split (response , ":" )
197
+ require .Len (t , split , 2 )
198
+ name := split [0 ]
199
+ var removed bool
200
+ names , removed = removeName (names , name )
201
+ // If no name was removed from the list, it means that the response
202
+ // does not contain the name of a known server.
203
+ if ! removed {
204
+ t .Fatalf ("received unexpected response from backend: %s" , name )
205
+ }
206
+ }
207
+ require .Len (t , names , 0 )
208
+ }
209
+
210
+ t .Log ("deleting the TCPRoute and verifying that all TCP connections are closed" )
211
+ require .NoError (t , gwclient .GatewayV1alpha2 ().TCPRoutes (corev1 .NamespaceDefault ).Delete (ctx , tcprouteSampleName , metav1.DeleteOptions {}))
212
+ require .Eventually (t , func () bool {
213
+ for _ , conn := range conns {
214
+ _ , err := conn .Write ([]byte ("blahhh\n " ))
215
+ require .NoError (t , err )
216
+ err = conn .SetReadDeadline (time .Now ().Add (time .Second * 3 ))
217
+ require .NoError (t , err )
218
+
219
+ reader := bufio .NewReader (conn )
220
+ _ , err = reader .ReadBytes (byte ('\n' ))
221
+ if err != nil {
222
+ if strings .Contains (err .Error (), "i/o timeout" ) {
223
+ continue
224
+ }
225
+ t .Logf ("received unexpected error waiting for TCPRoute to decomission: %s" , err )
226
+ }
227
+ return false
228
+ }
229
+ return true
230
+ }, time .Minute , time .Second )
231
+ }
232
+
233
+ func removeName (names []string , name string ) ([]string , bool ) {
234
+ for i , v := range names {
235
+ if v == name {
236
+ names = append (names [:i ], names [i + 1 :]... )
237
+ return names , true
238
+ }
239
+ }
240
+ return nil , false
241
+ }
242
+
243
+ func writeAndReadTCP (t * testing.T , conn net.Conn ) string {
244
+ t .Helper ()
245
+
246
+ t .Logf ("writing data to TCP connection with backend %s" , conn .RemoteAddr ().String ())
247
+ request := "wazzzaaaa"
248
+ _ , err := conn .Write ([]byte (request + "\n " ))
249
+ require .NoError (t , err )
250
+
251
+ t .Logf ("reading data from TCP connection with backend %s" , conn .RemoteAddr ().String ())
252
+ reader := bufio .NewReader (conn )
253
+ response , err := reader .ReadBytes (byte ('\n' ))
254
+ require .NoError (t , err )
255
+ return string (response )
256
+ }
0 commit comments