@@ -19,10 +19,16 @@ package service
19
19
import (
20
20
"context"
21
21
"testing"
22
+ "time"
22
23
23
24
corev1 "k8s.io/api/core/v1"
24
25
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26
+ "k8s.io/apimachinery/pkg/fields"
27
+ "k8s.io/apimachinery/pkg/labels"
28
+ "k8s.io/apimachinery/pkg/util/wait"
29
+ "k8s.io/client-go/informers"
25
30
clientset "k8s.io/client-go/kubernetes"
31
+ "k8s.io/client-go/tools/cache"
26
32
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
27
33
"k8s.io/kubernetes/test/integration/framework"
28
34
)
@@ -264,3 +270,139 @@ func Test_RemovingExternalIPsFromClusterIPServiceDropsExternalTrafficPolicy(t *t
264
270
t .Error ("service externalTrafficPolicy was not set for clusterIP Service with externalIPs" )
265
271
}
266
272
}
273
+
274
+ func Test_ServiceClusterIPSelector (t * testing.T ) {
275
+ server := kubeapiservertesting .StartTestServerOrDie (t , nil , nil , framework .SharedEtcd ())
276
+ defer server .TearDownFn ()
277
+
278
+ ctx , cancel := context .WithCancel (context .Background ())
279
+ defer cancel ()
280
+
281
+ client , err := clientset .NewForConfig (server .ClientConfig )
282
+ if err != nil {
283
+ t .Fatalf ("Error creating clientset: %v" , err )
284
+ }
285
+
286
+ ns := framework .CreateNamespaceOrDie (client , "test-external-name-drops-internal-traffic-policy" , t )
287
+ defer framework .DeleteNamespaceOrDie (client , ns , t )
288
+
289
+ // create headless service
290
+ service := & corev1.Service {
291
+ ObjectMeta : metav1.ObjectMeta {
292
+ Name : "test-headless" ,
293
+ Namespace : ns .Name ,
294
+ },
295
+ Spec : corev1.ServiceSpec {
296
+ ClusterIP : corev1 .ClusterIPNone ,
297
+ Type : corev1 .ServiceTypeClusterIP ,
298
+ Ports : []corev1.ServicePort {{
299
+ Port : int32 (80 ),
300
+ }},
301
+ Selector : map [string ]string {
302
+ "foo" : "bar" ,
303
+ },
304
+ },
305
+ }
306
+
307
+ _ , err = client .CoreV1 ().Services (ns .Name ).Create (ctx , service , metav1.CreateOptions {})
308
+ if err != nil {
309
+ t .Fatalf ("Error creating test service: %v" , err )
310
+ }
311
+
312
+ // informer to watch only non-headless services
313
+ kubeInformers := informers .NewSharedInformerFactoryWithOptions (client , 0 , informers .WithTweakListOptions (func (options * metav1.ListOptions ) {
314
+ options .FieldSelector = fields .OneTermNotEqualSelector ("spec.clusterIP" , corev1 .ClusterIPNone ).String ()
315
+ }))
316
+
317
+ serviceInformer := kubeInformers .Core ().V1 ().Services ().Informer ()
318
+ serviceLister := kubeInformers .Core ().V1 ().Services ().Lister ()
319
+ serviceHasSynced := serviceInformer .HasSynced
320
+ if _ , err = serviceInformer .AddEventHandler (
321
+ cache.ResourceEventHandlerFuncs {
322
+ AddFunc : func (obj interface {}) {
323
+ svc := obj .(* corev1.Service )
324
+ t .Logf ("Added Service %#v" , svc )
325
+ },
326
+ UpdateFunc : func (oldObj , newObj interface {}) {
327
+ oldSvc := oldObj .(* corev1.Service )
328
+ newSvc := newObj .(* corev1.Service )
329
+ t .Logf ("Updated Service %#v to %#v" , oldSvc , newSvc )
330
+ },
331
+ DeleteFunc : func (obj interface {}) {
332
+ svc := obj .(* corev1.Service )
333
+ t .Logf ("Deleted Service %#v" , svc )
334
+ },
335
+ },
336
+ ); err != nil {
337
+ t .Fatalf ("Error adding service informer handler: %v" , err )
338
+ }
339
+ kubeInformers .Start (ctx .Done ())
340
+ cache .WaitForCacheSync (ctx .Done (), serviceHasSynced )
341
+ svcs , err := serviceLister .List (labels .Everything ())
342
+ if err != nil {
343
+ t .Fatalf ("Error listing services: %v" , err )
344
+ }
345
+ // only the kubernetes.default service expected
346
+ if len (svcs ) != 1 || svcs [0 ].Name != "kubernetes" {
347
+ t .Fatalf ("expected 1 services, got %d" , len (svcs ))
348
+ }
349
+
350
+ // create a new service with ClusterIP
351
+ service2 := service .DeepCopy ()
352
+ service2 .Spec .ClusterIP = ""
353
+ service2 .Name = "test-clusterip"
354
+ _ , err = client .CoreV1 ().Services (ns .Name ).Create (ctx , service2 , metav1.CreateOptions {})
355
+ if err != nil {
356
+ t .Fatalf ("Error creating test service: %v" , err )
357
+ }
358
+
359
+ err = wait .PollUntilContextTimeout (ctx , 1 * time .Second , 10 * time .Second , true , func (ctx context.Context ) (done bool , err error ) {
360
+ svc , err := serviceLister .Services (service2 .Namespace ).Get (service2 .Name )
361
+ if svc == nil || err != nil {
362
+ return false , nil
363
+ }
364
+ return true , nil
365
+ })
366
+ if err != nil {
367
+ t .Fatalf ("Error waiting for test service test-clusterip: %v" , err )
368
+ }
369
+
370
+ // mutate the Service to drop the ClusterIP, theoretically ClusterIP is inmutable but ...
371
+ service .Spec .ExternalName = "test"
372
+ service .Spec .Type = corev1 .ServiceTypeExternalName
373
+ _ , err = client .CoreV1 ().Services (ns .Name ).Update (ctx , service , metav1.UpdateOptions {})
374
+ if err != nil {
375
+ t .Fatalf ("Error creating test service: %v" , err )
376
+ }
377
+
378
+ err = wait .PollUntilContextTimeout (ctx , 1 * time .Second , 10 * time .Second , true , func (ctx context.Context ) (done bool , err error ) {
379
+ svc , err := serviceLister .Services (service .Namespace ).Get (service .Name )
380
+ if svc == nil || err != nil {
381
+ return false , nil
382
+ }
383
+ return true , nil
384
+ })
385
+ if err != nil {
386
+ t .Fatalf ("Error waiting for test service without ClusterIP: %v" , err )
387
+ }
388
+
389
+ // mutate the Service to get the ClusterIP again
390
+ service .Spec .ExternalName = ""
391
+ service .Spec .ClusterIP = ""
392
+ service .Spec .Type = corev1 .ServiceTypeClusterIP
393
+ _ , err = client .CoreV1 ().Services (ns .Name ).Update (ctx , service , metav1.UpdateOptions {})
394
+ if err != nil {
395
+ t .Fatalf ("Error creating test service: %v" , err )
396
+ }
397
+
398
+ err = wait .PollUntilContextTimeout (ctx , 1 * time .Second , 10 * time .Second , true , func (ctx context.Context ) (done bool , err error ) {
399
+ svc , err := serviceLister .Services (service .Namespace ).Get (service .Name )
400
+ if svc == nil || err != nil {
401
+ return false , nil
402
+ }
403
+ return true , nil
404
+ })
405
+ if err != nil {
406
+ t .Fatalf ("Error waiting for test service with ClusterIP: %v" , err )
407
+ }
408
+ }
0 commit comments