@@ -30,7 +30,9 @@ import (
30
30
"k8s.io/kubernetes/test/e2e/framework"
31
31
e2edeploy "k8s.io/kubernetes/test/e2e/framework/deployment"
32
32
e2enode "k8s.io/kubernetes/test/e2e/framework/node"
33
+ e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
33
34
imageutils "k8s.io/kubernetes/test/utils/image"
35
+ netutils "k8s.io/utils/net"
34
36
)
35
37
36
38
// Tests for ipv6 dual stack feature
@@ -204,8 +206,152 @@ var _ = SIGDescribe("[Feature:IPv6DualStackAlphaFeature] [LinuxOnly]", func() {
204
206
205
207
assertNetworkConnectivity (f , * serverPods , * clientPods , "dualstack-test-client" , "80" )
206
208
})
209
+
210
+ ginkgo .It ("should create service with cluster ip from primary service range [Feature:IPv6DualStackAlphaFeature:Phase2]" , func () {
211
+ serviceName := "defaultclusterip"
212
+ ns := f .Namespace .Name
213
+ jig := e2eservice .NewTestJig (cs , serviceName )
214
+
215
+ defaultIPFamily := v1 .IPv4Protocol
216
+ if framework .TestContext .ClusterIsIPv6 () {
217
+ defaultIPFamily = v1 .IPv6Protocol
218
+ }
219
+
220
+ t := e2eservice .NewServerTest (cs , ns , serviceName )
221
+ defer func () {
222
+ defer ginkgo .GinkgoRecover ()
223
+ if errs := t .Cleanup (); len (errs ) != 0 {
224
+ framework .Failf ("errors in cleanup: %v" , errs )
225
+ }
226
+ }()
227
+
228
+ ginkgo .By ("creating service " + ns + "/" + serviceName + " with Service.Spec.IPFamily not set" )
229
+ service := createService (t .ServiceName , t .Namespace , t .Labels , nil )
230
+
231
+ jig .Labels = t .Labels
232
+ jig .CreateServicePods (cs , ns , 2 )
233
+ svc , err := t .CreateService (service )
234
+ framework .ExpectNoError (err , "failed to create service: %s in namespace: %s" , serviceName , ns )
235
+
236
+ validateNumOfServicePorts (svc , 2 )
237
+
238
+ // check the spec has been set to default ip family
239
+ validateServiceAndClusterIPFamily (svc , defaultIPFamily )
240
+
241
+ // ensure endpoint belong to same ipfamily as service
242
+ endpoint , err := cs .CoreV1 ().Endpoints (svc .Namespace ).Get (svc .Name , metav1.GetOptions {})
243
+ if err != nil {
244
+ framework .Failf ("Get endpoints for service %s/%s failed (%s)" , svc .Namespace , svc .Name , err )
245
+ }
246
+ validateEndpointsBelongToIPFamily (svc , endpoint , defaultIPFamily )
247
+ })
248
+
249
+ ginkgo .It ("should create service with ipv4 cluster ip [Feature:IPv6DualStackAlphaFeature:Phase2]" , func () {
250
+ serviceName := "ipv4clusterip"
251
+ ns := f .Namespace .Name
252
+ ipv4 := v1 .IPv4Protocol
253
+
254
+ jig := e2eservice .NewTestJig (cs , serviceName )
255
+
256
+ t := e2eservice .NewServerTest (cs , ns , serviceName )
257
+ defer func () {
258
+ defer ginkgo .GinkgoRecover ()
259
+ if errs := t .Cleanup (); len (errs ) != 0 {
260
+ framework .Failf ("errors in cleanup: %v" , errs )
261
+ }
262
+ }()
263
+
264
+ ginkgo .By ("creating service " + ns + "/" + serviceName + " with Service.Spec.IPFamily IPv4" + ns )
265
+ service := createService (t .ServiceName , t .Namespace , t .Labels , & ipv4 )
266
+
267
+ jig .Labels = t .Labels
268
+ jig .CreateServicePods (cs , ns , 2 )
269
+ svc , err := t .CreateService (service )
270
+ framework .ExpectNoError (err , "failed to create service: %s in namespace: %s" , serviceName , ns )
271
+
272
+ validateNumOfServicePorts (svc , 2 )
273
+
274
+ // check the spec has been set to IPv4 and cluster ip belong to IPv4 family
275
+ validateServiceAndClusterIPFamily (svc , ipv4 )
276
+
277
+ // ensure endpoints belong to same ipfamily as service
278
+ endpoint , err := cs .CoreV1 ().Endpoints (svc .Namespace ).Get (svc .Name , metav1.GetOptions {})
279
+ if err != nil {
280
+ framework .Failf ("Get endpoints for service %s/%s failed (%s)" , svc .Namespace , svc .Name , err )
281
+ }
282
+ validateEndpointsBelongToIPFamily (svc , endpoint , ipv4 )
283
+ })
284
+
285
+ ginkgo .It ("should create service with ipv6 cluster ip [Feature:IPv6DualStackAlphaFeature:Phase2]" , func () {
286
+ serviceName := "ipv6clusterip"
287
+ ns := f .Namespace .Name
288
+ ipv6 := v1 .IPv6Protocol
289
+
290
+ jig := e2eservice .NewTestJig (cs , serviceName )
291
+
292
+ t := e2eservice .NewServerTest (cs , ns , serviceName )
293
+ defer func () {
294
+ defer ginkgo .GinkgoRecover ()
295
+ if errs := t .Cleanup (); len (errs ) != 0 {
296
+ framework .Failf ("errors in cleanup: %v" , errs )
297
+ }
298
+ }()
299
+
300
+ ginkgo .By ("creating service " + ns + "/" + serviceName + " with Service.Spec.IPFamily IPv6" + ns )
301
+ service := createService (t .ServiceName , t .Namespace , t .Labels , & ipv6 )
302
+
303
+ jig .Labels = t .Labels
304
+ jig .CreateServicePods (cs , ns , 2 )
305
+ svc , err := t .CreateService (service )
306
+ framework .ExpectNoError (err , "failed to create service: %s in namespace: %s" , serviceName , ns )
307
+
308
+ validateNumOfServicePorts (svc , 2 )
309
+
310
+ // check the spec has been set to IPv6 and cluster ip belongs to IPv6 family
311
+ validateServiceAndClusterIPFamily (svc , ipv6 )
312
+
313
+ // ensure endpoints belong to same ipfamily as service
314
+ endpoint , err := cs .CoreV1 ().Endpoints (svc .Namespace ).Get (svc .Name , metav1.GetOptions {})
315
+ if err != nil {
316
+ framework .Failf ("Get endpoints for service %s/%s failed (%s)" , svc .Namespace , svc .Name , err )
317
+ }
318
+ validateEndpointsBelongToIPFamily (svc , endpoint , ipv6 )
319
+ })
207
320
})
208
321
322
+ func validateNumOfServicePorts (svc * v1.Service , expectedNumOfPorts int ) {
323
+ if len (svc .Spec .Ports ) != expectedNumOfPorts {
324
+ framework .Failf ("got unexpected len(Spec.Ports) for service: %v" , svc )
325
+ }
326
+ }
327
+
328
+ func validateServiceAndClusterIPFamily (svc * v1.Service , expectedIPFamily v1.IPFamily ) {
329
+ if svc .Spec .IPFamily == nil {
330
+ framework .Failf ("service ip family nil for service %s/%s" , svc .Namespace , svc .Name )
331
+ }
332
+ if * svc .Spec .IPFamily != expectedIPFamily {
333
+ framework .Failf ("ip family mismatch for service: %s/%s, expected: %s, actual: %s" , svc .Namespace , svc .Name , expectedIPFamily , * svc .Spec .IPFamily )
334
+ }
335
+
336
+ isIPv6ClusterIP := netutils .IsIPv6String (svc .Spec .ClusterIP )
337
+ if (expectedIPFamily == v1 .IPv4Protocol && isIPv6ClusterIP ) || (expectedIPFamily == v1 .IPv6Protocol && ! isIPv6ClusterIP ) {
338
+ framework .Failf ("got unexpected service ip %s, should belong to %s ip family" , svc .Spec .ClusterIP , expectedIPFamily )
339
+ }
340
+ }
341
+
342
+ func validateEndpointsBelongToIPFamily (svc * v1.Service , endpoint * v1.Endpoints , expectedIPFamily v1.IPFamily ) {
343
+ if len (endpoint .Subsets ) == 0 {
344
+ framework .Failf ("Endpoint has no subsets, cannot determine service ip family matches endpoints ip family for service %s/%s" , svc .Namespace , svc .Name )
345
+ }
346
+ for _ , ss := range endpoint .Subsets {
347
+ for _ , e := range ss .Addresses {
348
+ if (expectedIPFamily == v1 .IPv6Protocol && isIPv4 (e .IP )) || (expectedIPFamily == v1 .IPv4Protocol && netutils .IsIPv6String (e .IP )) {
349
+ framework .Failf ("service endpoint %s doesn't belong to %s ip family" , e .IP , expectedIPFamily )
350
+ }
351
+ }
352
+ }
353
+ }
354
+
209
355
func assertNetworkConnectivity (f * framework.Framework , serverPods v1.PodList , clientPods v1.PodList , containerName , port string ) {
210
356
// curl from each client pod to all server pods to assert connectivity
211
357
duration := "10s"
@@ -256,3 +402,30 @@ func isIPv4CIDR(cidr string) bool {
256
402
framework .ExpectNoError (err )
257
403
return isIPv4 (ip .String ())
258
404
}
405
+
406
+ // createService returns a service spec with defined arguments
407
+ func createService (name , ns string , labels map [string ]string , ipFamily * v1.IPFamily ) * v1.Service {
408
+ return & v1.Service {
409
+ ObjectMeta : metav1.ObjectMeta {
410
+ Name : name ,
411
+ Namespace : ns ,
412
+ },
413
+ Spec : v1.ServiceSpec {
414
+ Selector : labels ,
415
+ Type : v1 .ServiceTypeNodePort ,
416
+ IPFamily : ipFamily ,
417
+ Ports : []v1.ServicePort {
418
+ {
419
+ Name : "tcp-port" ,
420
+ Port : 53 ,
421
+ Protocol : v1 .ProtocolTCP ,
422
+ },
423
+ {
424
+ Name : "udp-port" ,
425
+ Port : 53 ,
426
+ Protocol : v1 .ProtocolUDP ,
427
+ },
428
+ },
429
+ },
430
+ }
431
+ }
0 commit comments