@@ -7,10 +7,12 @@ import (
7
7
"math/big"
8
8
"math/rand/v2"
9
9
"net"
10
+ "strconv"
10
11
"testing"
11
12
"time"
12
13
13
14
dockernetwork "github.com/docker/docker/api/types/network"
15
+ nadv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
14
16
corev1 "k8s.io/api/core/v1"
15
17
k8serrors "k8s.io/apimachinery/pkg/api/errors"
16
18
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -21,6 +23,8 @@ import (
21
23
k8sframework "k8s.io/kubernetes/test/e2e/framework"
22
24
"k8s.io/kubernetes/test/e2e/framework/config"
23
25
e2enode "k8s.io/kubernetes/test/e2e/framework/node"
26
+ e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output"
27
+ "k8s.io/utils/ptr"
24
28
25
29
"github.com/onsi/ginkgo/v2"
26
30
@@ -31,8 +35,6 @@ import (
31
35
"github.com/kubeovn/kube-ovn/test/e2e/framework/kind"
32
36
)
33
37
34
- const subnetProvider = "lb-svc-attachment.kube-system"
35
-
36
38
func init () {
37
39
klog .SetOutput (ginkgo .GinkgoWriter )
38
40
@@ -56,20 +58,27 @@ var _ = framework.SerialDescribe("[group:lb-svc]", func() {
56
58
57
59
var skip bool
58
60
var cs clientset.Interface
61
+ var podClient * framework.PodClient
59
62
var subnetClient * framework.SubnetClient
60
63
var serviceClient * framework.ServiceClient
61
64
var deploymentClient * framework.DeploymentClient
62
- var clusterName , subnetName , namespaceName , serviceName , deploymentName string
65
+ var nadClient * framework.NetworkAttachmentDefinitionClient
66
+ var provider , nadName , clusterName , subnetName , namespaceName , serviceName , deploymentName , serverPodName , clientPodName string
63
67
var dockerNetwork * dockernetwork.Inspect
64
68
var cidr , gateway string
65
69
ginkgo .BeforeEach (func () {
66
70
cs = f .ClientSet
71
+ podClient = f .PodClient ()
67
72
subnetClient = f .SubnetClient ()
68
73
serviceClient = f .ServiceClient ()
69
74
deploymentClient = f .DeploymentClient ()
75
+ nadClient = f .NetworkAttachmentDefinitionClient ()
70
76
namespaceName = f .Namespace .Name
77
+ nadName = "nad-" + framework .RandomSuffix ()
71
78
subnetName = "subnet-" + framework .RandomSuffix ()
72
79
serviceName = "service-" + framework .RandomSuffix ()
80
+ serverPodName = "pod-" + framework .RandomSuffix ()
81
+ clientPodName = "pod-" + framework .RandomSuffix ()
73
82
deploymentName = lbSvcDeploymentName (serviceName )
74
83
75
84
if skip {
@@ -96,6 +105,13 @@ var _ = framework.SerialDescribe("[group:lb-svc]", func() {
96
105
dockerNetwork = network
97
106
}
98
107
108
+ provider = fmt .Sprintf ("%s.%s" , nadName , namespaceName )
109
+
110
+ ginkgo .By ("Creating network attachment definition " + nadName )
111
+ nad := framework .MakeMacvlanNetworkAttachmentDefinition (nadName , namespaceName , "eth0" , "bridge" , provider , nil )
112
+ nad = nadClient .Create (nad )
113
+ framework .Logf ("created network attachment definition config:\n %s" , nad .Spec .Config )
114
+
99
115
ginkgo .By ("Creating subnet " + subnetName )
100
116
for _ , config := range dockerNetwork .IPAM .Config {
101
117
if util .CheckProtocol (config .Subnet ) == apiv1 .ProtocolIPv4 {
@@ -112,38 +128,54 @@ var _ = framework.SerialDescribe("[group:lb-svc]", func() {
112
128
}
113
129
}
114
130
subnet := framework .MakeSubnet (subnetName , "" , cidr , gateway , "" , "" , excludeIPs , nil , []string {namespaceName })
115
- subnet .Spec .Provider = subnetProvider
131
+ subnet .Spec .Provider = provider
116
132
_ = subnetClient .Create (subnet )
117
133
})
118
134
ginkgo .AfterEach (func () {
119
- ginkgo .By ("Deleting deployment " + deploymentName )
120
- deploymentClient .DeleteSync (deploymentName )
135
+ ginkgo .By ("Deleting pod " + clientPodName )
136
+ podClient .DeleteSync (clientPodName )
137
+
138
+ ginkgo .By ("Deleting pod " + serverPodName )
139
+ podClient .DeleteSync (serverPodName )
121
140
122
141
ginkgo .By ("Deleting service " + serviceName )
123
142
serviceClient .DeleteSync (serviceName )
124
143
144
+ ginkgo .By ("Deleting deployment " + deploymentName )
145
+ deploymentClient .DeleteSync (deploymentName )
146
+
125
147
ginkgo .By ("Deleting subnet " + subnetName )
126
148
subnetClient .DeleteSync (subnetName )
149
+
150
+ ginkgo .By ("Deleting network attachment definition " + nadName )
151
+ nadClient .Delete (nadName )
127
152
})
128
153
129
154
framework .ConformanceIt ("should allocate dynamic external IP for service" , func () {
155
+ ginkgo .By ("Creating server pod " + serverPodName )
156
+ labels := map [string ]string {"app" : serviceName }
157
+ port := 8000 + rand .Int32N (1000 )
158
+ args := []string {"netexec" , "--http-port" , strconv .Itoa (int (port ))}
159
+ serverPod := framework .MakePod (namespaceName , serverPodName , labels , nil , framework .AgnhostImage , nil , args )
160
+ _ = podClient .CreateSync (serverPod )
161
+
130
162
ginkgo .By ("Creating service " + serviceName )
131
163
ports := []corev1.ServicePort {{
132
164
Name : "tcp" ,
133
165
Protocol : corev1 .ProtocolTCP ,
134
- Port : 80 ,
135
- TargetPort : intstr .FromInt32 (80 ),
166
+ Port : port ,
167
+ TargetPort : intstr .FromInt32 (port ),
136
168
}}
137
169
annotations := map [string ]string {
138
- subnetProvider + ".kubernetes.io/logical_switch" : subnetName ,
170
+ util . AttachmentProvider : provider ,
139
171
}
140
- selector := map [ string ] string { "app" : "lb-svc-dynamic" }
141
- service := framework . MakeService ( serviceName , corev1 . ServiceTypeLoadBalancer , annotations , selector , ports , corev1 . ServiceAffinityNone )
142
- _ = serviceClient .CreateSync (service , func (s * corev1.Service ) (bool , error ) {
172
+ service := framework . MakeService ( serviceName , corev1 . ServiceTypeLoadBalancer , annotations , labels , ports , corev1 . ServiceAffinityNone )
173
+ service . Spec . AllocateLoadBalancerNodePorts = ptr . To ( false )
174
+ service = serviceClient .CreateSync (service , func (s * corev1.Service ) (bool , error ) {
143
175
return len (s .Spec .ClusterIPs ) != 0 , nil
144
176
}, "cluster ips are not empty" )
145
177
146
- ginkgo .By ("Waiting for deployment " + deploymentName + " to be ready" )
178
+ ginkgo .By ("Waiting for LB deployment " + deploymentName + " to be ready" )
147
179
framework .WaitUntil (2 * time .Second , time .Minute , func (ctx context.Context ) (bool , error ) {
148
180
_ , err := deploymentClient .DeploymentInterface .Get (ctx , deploymentName , metav1.GetOptions {})
149
181
if err == nil {
@@ -164,44 +196,91 @@ var _ = framework.SerialDescribe("[group:lb-svc]", func() {
164
196
framework .ExpectNoError (err )
165
197
framework .ExpectHaveLen (pods .Items , 1 )
166
198
167
- ginkgo .By ("Checking pod annotations" )
168
- key := fmt . Sprintf ( util . AllocatedAnnotationTemplate , subnetProvider )
169
- framework . ExpectHaveKeyWithValue ( pods . Items [ 0 ]. Annotations , key , "true" )
170
- cidrKey := fmt . Sprintf ( util . CidrAnnotationTemplate , subnetProvider )
171
- ipKey := fmt .Sprintf (util .IPAddressAnnotationTemplate , subnetProvider )
172
- framework . ExpectHaveKey ( pods . Items [ 0 ]. Annotations , cidrKey )
173
- framework .ExpectHaveKey (pods . Items [ 0 ]. Annotations , ipKey )
174
- cidr := pods . Items [ 0 ] .Annotations [ cidrKey ]
175
- ip := pods . Items [ 0 ] .Annotations [ipKey ]
176
- framework .ExpectTrue ( util . CIDRContainIP ( cidr , ip ) )
177
-
178
- ginkgo .By ("Checking service external IP " )
199
+ ginkgo .By ("Checking LB pod annotations" )
200
+ pod := & pods . Items [ 0 ]
201
+ key := fmt . Sprintf ( util . AllocatedAnnotationTemplate , provider )
202
+ framework . ExpectHaveKeyWithValue ( pod . Annotations , key , "true" )
203
+ cidrKey := fmt .Sprintf (util .CidrAnnotationTemplate , provider )
204
+ ipKey := fmt . Sprintf ( util . IPAddressAnnotationTemplate , provider )
205
+ framework .ExpectHaveKey (pod . Annotations , cidrKey )
206
+ framework . ExpectHaveKey ( pod .Annotations , ipKey )
207
+ lbIP := pod .Annotations [ipKey ]
208
+ framework .ExpectIPInCIDR ( lbIP , pod . Annotations [ cidrKey ] )
209
+
210
+ ginkgo .By ("Checking service status " )
179
211
framework .WaitUntil (2 * time .Second , time .Minute , func (_ context.Context ) (bool , error ) {
180
212
service = serviceClient .Get (serviceName )
181
213
return len (service .Status .LoadBalancer .Ingress ) != 0 , nil
182
214
}, ".status.loadBalancer.ingress is not empty" )
183
- framework .ExpectEqual (service .Status .LoadBalancer .Ingress [0 ].IP , ip )
215
+ framework .ExpectHaveLen (service .Status .LoadBalancer .Ingress , 1 )
216
+ framework .ExpectEqual (service .Status .LoadBalancer .Ingress [0 ].IP , lbIP )
217
+
218
+ ginkgo .By ("Creating client pod " + clientPodName )
219
+ annotations = map [string ]string {nadv1 .NetworkAttachmentAnnot : fmt .Sprintf ("%s/%s" , namespaceName , nadName )}
220
+ cmd := []string {"sh" , "-c" , "sleep infinity" }
221
+ clientPod := framework .MakePod (namespaceName , clientPodName , nil , annotations , f .KubeOVNImage , cmd , nil )
222
+ clientPod = podClient .CreateSync (clientPod )
223
+
224
+ ginkgo .By ("Checking service connectivity from client pod " + clientPodName )
225
+ curlCmd := fmt .Sprintf ("curl -q -s --connect-timeout 2 --max-time 2 %s/clientip" , util .JoinHostPort (lbIP , port ))
226
+ ginkgo .By (fmt .Sprintf (`Executing %q in pod %s/%s` , curlCmd , clientPod .Namespace , clientPod .Name ))
227
+ _ = e2epodoutput .RunHostCmdOrDie (clientPod .Namespace , clientPod .Name , curlCmd )
228
+
229
+ ginkgo .By ("Deleting lb svc pod " + pod .Name )
230
+ podClient .DeleteSync (pod .Name )
231
+
232
+ ginkgo .By ("Waiting for LB deployment " + deploymentName + " to be ready" )
233
+ err = deploymentClient .WaitToComplete (deployment )
234
+ framework .ExpectNoError (err , "deployment failed to complete" )
235
+
236
+ ginkgo .By ("Getting pods for deployment " + deploymentName )
237
+ pods , err = deploymentClient .GetPods (deployment )
238
+ framework .ExpectNoError (err )
239
+ framework .ExpectHaveLen (pods .Items , 1 )
240
+ lbIP = pods .Items [0 ].Annotations [ipKey ]
241
+
242
+ ginkgo .By ("Checking service connectivity from client pod " + clientPodName )
243
+ curlCmd = fmt .Sprintf ("curl -q -s --connect-timeout 2 --max-time 2 %s/clientip" , util .JoinHostPort (lbIP , port ))
244
+ framework .WaitUntil (2 * time .Second , 30 * time .Second , func (_ context.Context ) (bool , error ) {
245
+ ginkgo .By (fmt .Sprintf (`Executing %q in pod %s/%s` , curlCmd , clientPod .Namespace , clientPod .Name ))
246
+ _ , err = e2epodoutput .RunHostCmd (clientPod .Namespace , clientPod .Name , curlCmd )
247
+ return err == nil , nil
248
+ }, "" )
249
+
250
+ ginkgo .By ("Deleting service " + serviceName )
251
+ serviceClient .DeleteSync (serviceName )
252
+
253
+ ginkgo .By ("Waiting for LB deployment " + deploymentName + " to be deleted automatically" )
254
+ err = deploymentClient .WaitToDisappear (deploymentName , 2 * time .Second , 2 * time .Minute )
255
+ framework .ExpectNoError (err , "deployment failed to disappear" )
184
256
})
185
257
186
258
framework .ConformanceIt ("should allocate static external IP for service" , func () {
259
+ ginkgo .By ("Creating server pod " + serverPodName )
260
+ labels := map [string ]string {"app" : serviceName }
261
+ port := 8000 + rand .Int32N (1000 )
262
+ args := []string {"netexec" , "--http-port" , strconv .Itoa (int (port ))}
263
+ serverPod := framework .MakePod (namespaceName , serverPodName , labels , nil , framework .AgnhostImage , nil , args )
264
+ _ = podClient .CreateSync (serverPod )
265
+
187
266
ginkgo .By ("Creating service " + serviceName )
188
- base := util .IP2BigInt (gateway )
189
- lbIP := util .BigInt2Ip (base .Add (base , big .NewInt (50 + rand .Int64N (50 ))))
190
267
ports := []corev1.ServicePort {{
191
268
Name : "tcp" ,
192
269
Protocol : corev1 .ProtocolTCP ,
193
- Port : 80 ,
194
- TargetPort : intstr .FromInt32 (80 ),
270
+ Port : port ,
271
+ TargetPort : intstr .FromInt32 (port ),
195
272
}}
196
273
annotations := map [string ]string {
197
- subnetProvider + ".kubernetes.io/logical_switch" : subnetName ,
274
+ util . AttachmentProvider : provider ,
198
275
}
199
- selector := map [string ]string {"app" : "lb-svc-static" }
200
- service := framework .MakeService (serviceName , corev1 .ServiceTypeLoadBalancer , annotations , selector , ports , corev1 .ServiceAffinityNone )
276
+ base := util .IP2BigInt (gateway )
277
+ lbIP := util .BigInt2Ip (base .Add (base , big .NewInt (50 + rand .Int64N (50 ))))
278
+ service := framework .MakeService (serviceName , corev1 .ServiceTypeLoadBalancer , annotations , labels , ports , corev1 .ServiceAffinityNone )
201
279
service .Spec .LoadBalancerIP = lbIP
280
+ service .Spec .AllocateLoadBalancerNodePorts = ptr .To (false )
202
281
_ = serviceClient .Create (service )
203
282
204
- ginkgo .By ("Waiting for deployment " + deploymentName + " to be ready" )
283
+ ginkgo .By ("Waiting for LB deployment " + deploymentName + " to be ready" )
205
284
framework .WaitUntil (2 * time .Second , time .Minute , func (ctx context.Context ) (bool , error ) {
206
285
_ , err := deploymentClient .DeploymentInterface .Get (ctx , deploymentName , metav1.GetOptions {})
207
286
if err == nil {
@@ -221,19 +300,54 @@ var _ = framework.SerialDescribe("[group:lb-svc]", func() {
221
300
framework .ExpectNoError (err )
222
301
framework .ExpectHaveLen (pods .Items , 1 )
223
302
224
- ginkgo .By ("Checking pod annotations" )
225
- key := fmt .Sprintf (util .AllocatedAnnotationTemplate , subnetProvider )
226
- framework .ExpectHaveKeyWithValue (pods .Items [0 ].Annotations , key , "true" )
227
- ipKey := fmt .Sprintf (util .IPAddressAnnotationTemplate , subnetProvider )
228
- framework .ExpectHaveKeyWithValue (pods .Items [0 ].Annotations , ipKey , lbIP )
229
- cidr := pods .Items [0 ].Annotations [fmt .Sprintf (util .CidrAnnotationTemplate , subnetProvider )]
230
- framework .ExpectTrue (util .CIDRContainIP (cidr , lbIP ))
303
+ ginkgo .By ("Checking LB pod annotations" )
304
+ pod := & pods .Items [0 ]
305
+ key := fmt .Sprintf (util .AllocatedAnnotationTemplate , provider )
306
+ framework .ExpectHaveKeyWithValue (pod .Annotations , key , "true" )
307
+ ipKey := fmt .Sprintf (util .IPAddressAnnotationTemplate , provider )
308
+ framework .ExpectHaveKeyWithValue (pod .Annotations , ipKey , lbIP )
309
+ cidrKey := fmt .Sprintf (util .CidrAnnotationTemplate , provider )
310
+ framework .ExpectHaveKey (pod .Annotations , cidrKey )
311
+ framework .ExpectIPInCIDR (lbIP , pod .Annotations [cidrKey ])
231
312
232
- ginkgo .By ("Checking service external IP " )
313
+ ginkgo .By ("Checking service status " )
233
314
framework .WaitUntil (2 * time .Second , time .Minute , func (_ context.Context ) (bool , error ) {
234
315
service = serviceClient .Get (serviceName )
235
316
return len (service .Status .LoadBalancer .Ingress ) != 0 , nil
236
317
}, ".status.loadBalancer.ingress is not empty" )
318
+ framework .ExpectHaveLen (service .Status .LoadBalancer .Ingress , 1 )
237
319
framework .ExpectEqual (service .Status .LoadBalancer .Ingress [0 ].IP , lbIP )
320
+
321
+ ginkgo .By ("Creating client pod " + clientPodName )
322
+ annotations = map [string ]string {nadv1 .NetworkAttachmentAnnot : fmt .Sprintf ("%s/%s" , namespaceName , nadName )}
323
+ cmd := []string {"sh" , "-c" , "sleep infinity" }
324
+ clientPod := framework .MakePod (namespaceName , clientPodName , nil , annotations , f .KubeOVNImage , cmd , nil )
325
+ clientPod = podClient .CreateSync (clientPod )
326
+
327
+ ginkgo .By ("Checking service connectivity from client pod " + clientPodName )
328
+ curlCmd := fmt .Sprintf ("curl -q -s --connect-timeout 2 --max-time 2 %s/clientip" , util .JoinHostPort (lbIP , port ))
329
+ ginkgo .By (fmt .Sprintf (`Executing %q in pod %s/%s` , curlCmd , clientPod .Namespace , clientPod .Name ))
330
+ _ = e2epodoutput .RunHostCmdOrDie (clientPod .Namespace , clientPod .Name , curlCmd )
331
+
332
+ ginkgo .By ("Deleting lb svc pod " + pod .Name )
333
+ podClient .DeleteSync (pod .Name )
334
+
335
+ ginkgo .By ("Waiting for LB deployment " + deploymentName + " to be ready" )
336
+ err = deploymentClient .WaitToComplete (deployment )
337
+ framework .ExpectNoError (err , "deployment failed to complete" )
338
+
339
+ ginkgo .By ("Checking service connectivity from client pod " + clientPodName )
340
+ framework .WaitUntil (2 * time .Second , 30 * time .Second , func (_ context.Context ) (bool , error ) {
341
+ ginkgo .By (fmt .Sprintf (`Executing %q in pod %s/%s` , curlCmd , clientPod .Namespace , clientPod .Name ))
342
+ _ , err = e2epodoutput .RunHostCmd (clientPod .Namespace , clientPod .Name , curlCmd )
343
+ return err == nil , nil
344
+ }, "" )
345
+
346
+ ginkgo .By ("Deleting service " + serviceName )
347
+ serviceClient .DeleteSync (serviceName )
348
+
349
+ ginkgo .By ("Waiting for LB deployment " + deploymentName + " to be deleted automatically" )
350
+ err = deploymentClient .WaitToDisappear (deploymentName , 2 * time .Second , 2 * time .Minute )
351
+ framework .ExpectNoError (err , "deployment failed to disappear" )
238
352
})
239
353
})
0 commit comments