Skip to content

Commit e790e9d

Browse files
committed
tcproute: add tests for round-robin load balancing
Signed-off-by: Sanskar Jaiswal <[email protected]>
1 parent 31d8a2d commit e790e9d

File tree

5 files changed

+241
-17
lines changed

5 files changed

+241
-17
lines changed

config/samples/tcproute/server.yaml

+2-4
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@ spec:
1616
spec:
1717
containers:
1818
- name: server
19-
image: ghcr.io/shaneutt/malutki
19+
image: istio/tcp-echo-server:1.1
2020
imagePullPolicy: IfNotPresent
21-
env:
22-
- name: LISTEN_PORT
23-
value: "8080"
21+
args: [ "8080", "blixt-tcproute-sample:" ]
2422
ports:
2523
- containerPort: 8080
2624
protocol: TCP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: kustomize.config.k8s.io/v1beta1
2+
kind: Kustomization
3+
images:
4+
- name: ghcr.io/kong/blixt-udp-test-server
5+
newTag: integration-tests
6+
resources:
7+
- ../../samples/tcproute
8+
- server.yaml
9+
patches:
10+
- path: patch.yaml

config/tests/tcproute-rr/patch.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: gateway.networking.k8s.io/v1alpha2
2+
kind: TCPRoute
3+
metadata:
4+
name: blixt-tcproute-sample
5+
spec:
6+
parentRefs:
7+
- name: blixt-tcproute-sample
8+
port: 8080
9+
rules:
10+
- backendRefs:
11+
- name: blixt-tcproute-sample
12+
port: 8080
13+
- backendRefs:
14+
- name: tcproute-rr-v1
15+
port: 8080
16+
- backendRefs:
17+
- name: tcproute-rr-v2
18+
port: 8080

config/tests/tcproute-rr/server.yaml

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
---
2+
apiVersion: apps/v1
3+
kind: Deployment
4+
metadata:
5+
name: tcproute-rr-v1
6+
labels:
7+
app: tcproute-rr-v1
8+
spec:
9+
selector:
10+
matchLabels:
11+
app: tcproute-rr-v1
12+
template:
13+
metadata:
14+
labels:
15+
app: tcproute-rr-v1
16+
spec:
17+
containers:
18+
- name: tcp-echo
19+
image: istio/tcp-echo-server:1.1
20+
imagePullPolicy: IfNotPresent
21+
args: [ "8080", "tcproute-rr-v1:" ]
22+
ports:
23+
- containerPort: 8080
24+
---
25+
apiVersion: apps/v1
26+
kind: Deployment
27+
metadata:
28+
name: tcproute-rr-v2
29+
labels:
30+
app: tcproute-rr-v2
31+
spec:
32+
selector:
33+
matchLabels:
34+
app: tcproute-rr-v2
35+
template:
36+
metadata:
37+
labels:
38+
app: tcproute-rr-v2
39+
spec:
40+
containers:
41+
- name: tcp-echo
42+
image: istio/tcp-echo-server:1.1
43+
imagePullPolicy: IfNotPresent
44+
args: [ "8080", "tcproute-rr-v2:" ]
45+
ports:
46+
- containerPort: 8080
47+
---
48+
apiVersion: v1
49+
kind: Service
50+
metadata:
51+
labels:
52+
app: tcproute-rr-v1
53+
name: tcproute-rr-v1
54+
spec:
55+
ports:
56+
- name: tcp
57+
port: 8080
58+
protocol: TCP
59+
selector:
60+
app: tcproute-rr-v1
61+
type: ClusterIP
62+
---
63+
apiVersion: v1
64+
kind: Service
65+
metadata:
66+
labels:
67+
app: tcproute-rr-v2
68+
name: tcproute-rr-v2
69+
spec:
70+
ports:
71+
- name: tcp
72+
port: 8080
73+
protocol: TCP
74+
selector:
75+
app: tcproute-rr-v2
76+
type: ClusterIP

test/integration/tcproute_test.go

+135-13
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ limitations under the License.
2020
package integration
2121

2222
import (
23+
"bufio"
2324
"context"
2425
"fmt"
25-
"net/http"
26+
"net"
2627
"strings"
2728
"testing"
2829
"time"
@@ -38,9 +39,12 @@ import (
3839

3940
const (
4041
tcprouteSampleKustomize = "../../config/tests/tcproute"
42+
tcprouteRRKustomize = "../../config/tests/tcproute-rr"
4143
tcprouteSampleName = "blixt-tcproute-sample"
4244
)
4345

46+
var tcpServerNames = []string{"blixt-tcproute-sample", "tcproute-rr-v1", "tcproute-rr-v2"}
47+
4448
func TestTCPRouteBasics(t *testing.T) {
4549
tcpRouteBasicsCleanupKey := "tcproutebasics"
4650
defer func() {
@@ -69,38 +73,156 @@ func TestTCPRouteBasics(t *testing.T) {
6973
require.Equal(t, gatewayv1beta1.IPAddressType, *gw.Status.Addresses[0].Type)
7074
gwaddr := fmt.Sprintf("%s:8080", gw.Status.Addresses[0].Value)
7175

72-
t.Log("waiting for HTTP server to be available")
76+
t.Log("waiting for TCP server to be available")
7377
require.Eventually(t, func() bool {
7478
server, err := env.Cluster().Client().AppsV1().Deployments(corev1.NamespaceDefault).Get(ctx, tcprouteSampleName, metav1.GetOptions{})
7579
require.NoError(t, err)
7680
return server.Status.AvailableReplicas > 0
7781
}, time.Minute, time.Second)
7882

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
8185
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)
8388
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)
8590
return false
8691
}
87-
defer resp.Body.Close()
88-
return resp.StatusCode == http.StatusTeapot
92+
return true
8993
}, time.Minute*5, time.Second)
9094

91-
t.Log("deleting the TCPRoute and verifying that HTTP traffic stops")
95+
writeAndReadTCP(t, conn, tcpServerNames[0])
96+
97+
t.Log("deleting the TCPRoute and verifying that TCP connection is closed")
9298
require.NoError(t, gwclient.GatewayV1alpha2().TCPRoutes(corev1.NamespaceDefault).Delete(ctx, tcprouteSampleName, metav1.DeleteOptions{}))
93-
httpc = http.Client{Timeout: time.Second * 3}
9499
require.Eventually(t, func() bool {
95-
resp, err := httpc.Get(fmt.Sprintf("http://%s/status/%d", gwaddr, http.StatusTeapot))
100+
_, err := conn.Write([]byte("blahhh\n"))
101+
require.NoError(t, err)
102+
103+
err = conn.SetReadDeadline(time.Now().Add(time.Second * 3))
104+
require.NoError(t, err)
105+
reader := bufio.NewReader(conn)
106+
_, err = reader.ReadBytes(byte('\n'))
96107
if err != nil {
97-
if strings.Contains(err.Error(), "context deadline exceeded") {
108+
if strings.Contains(err.Error(), "i/o timeout") {
98109
return true
99110
}
100111
t.Logf("received unexpected error waiting for TCPRoute to decomission: %s", err)
101112
return false
102113
}
103-
defer resp.Body.Close()
104114
return false
105115
}, time.Minute, time.Second)
106116
}
117+
118+
func TestTCPRouteRoundRobin(t *testing.T) {
119+
tcpRouteRRCleanupKey := "tcprouterr"
120+
defer func() {
121+
testutils.DumpDiagnosticsIfFailed(ctx, t, env.Cluster())
122+
if err := runCleanup(tcpRouteRRCleanupKey); err != nil {
123+
t.Errorf("cleanup failed: %s", err)
124+
}
125+
}()
126+
127+
t.Log("deploying config/samples/tcproute-rr kustomize")
128+
require.NoError(t, clusters.KustomizeDeployForCluster(ctx, env.Cluster(), tcprouteRRKustomize))
129+
addCleanup(tcpRouteRRCleanupKey, func(ctx context.Context) error {
130+
cleanupLog("cleaning up config/samples/tcproute-rr kustomize")
131+
return clusters.KustomizeDeleteForCluster(ctx, env.Cluster(), tcprouteRRKustomize, "--ignore-not-found=true")
132+
})
133+
134+
t.Log("waiting for Gateway to have an address")
135+
var gw *gatewayv1beta1.Gateway
136+
require.Eventually(t, func() bool {
137+
var err error
138+
gw, err = gwclient.GatewayV1beta1().Gateways(corev1.NamespaceDefault).Get(ctx, tcprouteSampleName, metav1.GetOptions{})
139+
require.NoError(t, err)
140+
return len(gw.Status.Addresses) > 0
141+
}, time.Minute, time.Second)
142+
require.NotNil(t, gw.Status.Addresses[0].Type)
143+
require.Equal(t, gatewayv1beta1.IPAddressType, *gw.Status.Addresses[0].Type)
144+
gwaddr := fmt.Sprintf("%s:8080", gw.Status.Addresses[0].Value)
145+
146+
t.Log("waiting for TCP servers to be available")
147+
labelSelector := metav1.LabelSelector{
148+
MatchExpressions: []metav1.LabelSelectorRequirement{
149+
{
150+
Key: "app",
151+
Operator: metav1.LabelSelectorOpIn,
152+
Values: tcpServerNames,
153+
},
154+
},
155+
}
156+
require.Eventually(t, func() bool {
157+
servers, err := env.Cluster().Client().AppsV1().Deployments(corev1.NamespaceDefault).List(ctx, metav1.ListOptions{
158+
LabelSelector: metav1.FormatLabelSelector(&labelSelector),
159+
})
160+
require.NoError(t, err)
161+
for _, server := range servers.Items {
162+
if server.Status.AvailableReplicas <= 0 {
163+
return false
164+
}
165+
}
166+
return true
167+
}, time.Minute, time.Second)
168+
169+
t.Log("verifying TCP connectivity to the servers")
170+
var conn1 net.Conn
171+
require.Eventually(t, func() bool {
172+
var err error
173+
conn1, err = net.Dial("tcp", gwaddr)
174+
if err != nil {
175+
t.Logf("received error connecting to TCP server: [%s], retrying...", err)
176+
return false
177+
}
178+
return true
179+
}, time.Minute*5, time.Second)
180+
conn2, err := net.Dial("tcp", gwaddr)
181+
require.NoError(t, err)
182+
conn3, err := net.Dial("tcp", gwaddr)
183+
require.NoError(t, err)
184+
conns := []net.Conn{conn1, conn2, conn3}
185+
186+
for c := 0; c < 2; c++ {
187+
for i, conn := range conns {
188+
writeAndReadTCP(t, conn, tcpServerNames[i])
189+
}
190+
}
191+
192+
t.Log("deleting the TCPRoute and verifying that all TCP connections are closed")
193+
require.NoError(t, gwclient.GatewayV1alpha2().TCPRoutes(corev1.NamespaceDefault).Delete(ctx, tcprouteSampleName, metav1.DeleteOptions{}))
194+
require.Eventually(t, func() bool {
195+
for _, conn := range conns {
196+
_, err := conn.Write([]byte("blahhh\n"))
197+
require.NoError(t, err)
198+
err = conn.SetReadDeadline(time.Now().Add(time.Second * 3))
199+
require.NoError(t, err)
200+
201+
reader := bufio.NewReader(conn)
202+
_, err = reader.ReadBytes(byte('\n'))
203+
if err != nil {
204+
if strings.Contains(err.Error(), "i/o timeout") {
205+
continue
206+
}
207+
t.Logf("received unexpected error waiting for TCPRoute to decomission: %s", err)
208+
}
209+
return false
210+
}
211+
return true
212+
}, time.Minute, time.Second)
213+
}
214+
215+
func writeAndReadTCP(t *testing.T, conn net.Conn, prefix string) {
216+
t.Helper()
217+
218+
t.Logf("writing data to TCP connection with server %s", prefix)
219+
request := "wazzzaaaa"
220+
_, err := conn.Write([]byte(request + "\n"))
221+
require.NoError(t, err)
222+
223+
t.Logf("reading data from TCP connection with server %s", prefix)
224+
reader := bufio.NewReader(conn)
225+
response, err := reader.ReadBytes(byte('\n'))
226+
require.NoError(t, err)
227+
require.Contains(t, string(response), fmt.Sprintf("%s: %s", prefix, string(request)))
228+
}

0 commit comments

Comments
 (0)