k8s.io/kubernetes@v1.29.3/pkg/registry/core/service/ipallocator/controller/repairip_test.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package controller 18 19 import ( 20 "fmt" 21 "testing" 22 "time" 23 24 "github.com/google/go-cmp/cmp" 25 v1 "k8s.io/api/core/v1" 26 networkingv1alpha1 "k8s.io/api/networking/v1alpha1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/util/wait" 30 "k8s.io/client-go/informers" 31 "k8s.io/client-go/kubernetes/fake" 32 k8stesting "k8s.io/client-go/testing" 33 "k8s.io/client-go/tools/cache" 34 "k8s.io/client-go/tools/events" 35 "k8s.io/kubernetes/pkg/registry/core/service/ipallocator" 36 ) 37 38 var ( 39 serviceCIDRv4 = "10.0.0.0/16" 40 serviceCIDRv6 = "2001:db8::/64" 41 ) 42 43 type fakeRepair struct { 44 *RepairIPAddress 45 serviceStore cache.Store 46 ipAddressStore cache.Store 47 serviceCIDRStore cache.Store 48 } 49 50 func newFakeRepair() (*fake.Clientset, *fakeRepair) { 51 fakeClient := fake.NewSimpleClientset() 52 53 informerFactory := informers.NewSharedInformerFactory(fakeClient, 0*time.Second) 54 serviceInformer := informerFactory.Core().V1().Services() 55 serviceIndexer := serviceInformer.Informer().GetIndexer() 56 57 serviceCIDRInformer := informerFactory.Networking().V1alpha1().ServiceCIDRs() 58 serviceCIDRIndexer := serviceCIDRInformer.Informer().GetIndexer() 59 60 ipInformer := informerFactory.Networking().V1alpha1().IPAddresses() 61 ipIndexer := ipInformer.Informer().GetIndexer() 62 63 fakeClient.PrependReactor("create", "ipaddresses", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) { 64 ip := action.(k8stesting.CreateAction).GetObject().(*networkingv1alpha1.IPAddress) 65 err := ipIndexer.Add(ip) 66 return false, ip, err 67 })) 68 fakeClient.PrependReactor("update", "ipaddresses", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) { 69 ip := action.(k8stesting.UpdateAction).GetObject().(*networkingv1alpha1.IPAddress) 70 return false, ip, fmt.Errorf("IPAddress is inmutable after creation") 71 })) 72 fakeClient.PrependReactor("delete", "ipaddresses", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) { 73 ip := action.(k8stesting.DeleteAction).GetName() 74 err := ipIndexer.Delete(ip) 75 return false, &networkingv1alpha1.IPAddress{}, err 76 })) 77 78 r := NewRepairIPAddress(0*time.Second, 79 fakeClient, 80 serviceInformer, 81 serviceCIDRInformer, 82 ipInformer, 83 ) 84 return fakeClient, &fakeRepair{r, serviceIndexer, ipIndexer, serviceCIDRIndexer} 85 } 86 87 func TestRepairServiceIP(t *testing.T) { 88 tests := []struct { 89 name string 90 svcs []*v1.Service 91 ipAddresses []*networkingv1alpha1.IPAddress 92 cidrs []*networkingv1alpha1.ServiceCIDR 93 expectedIPs []string 94 actions [][]string // verb and resource 95 events []string 96 }{ 97 { 98 name: "no changes needed single stack", 99 svcs: []*v1.Service{newService("test-svc", []string{"10.0.1.1"})}, 100 ipAddresses: []*networkingv1alpha1.IPAddress{ 101 newIPAddress("10.0.1.1", newService("test-svc", []string{"10.0.1.1"})), 102 }, 103 cidrs: []*networkingv1alpha1.ServiceCIDR{ 104 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 105 }, 106 expectedIPs: []string{"10.0.1.1"}, 107 actions: [][]string{}, 108 events: []string{}, 109 }, 110 { 111 name: "no changes needed dual stack", 112 svcs: []*v1.Service{newService("test-svc", []string{"10.0.1.1", "2001:db8::10"})}, 113 ipAddresses: []*networkingv1alpha1.IPAddress{ 114 newIPAddress("10.0.1.1", newService("test-svc", []string{"10.0.1.1"})), 115 newIPAddress("2001:db8::10", newService("test-svc", []string{"2001:db8::10"})), 116 }, 117 cidrs: []*networkingv1alpha1.ServiceCIDR{ 118 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 119 }, 120 expectedIPs: []string{"10.0.1.1", "2001:db8::10"}, 121 actions: [][]string{}, 122 events: []string{}, 123 }, 124 { 125 name: "no changes needed dual stack multiple cidrs", 126 svcs: []*v1.Service{newService("test-svc", []string{"192.168.0.1", "2001:db8:a:b::10"})}, 127 ipAddresses: []*networkingv1alpha1.IPAddress{ 128 newIPAddress("192.168.0.1", newService("test-svc", []string{"192.168.0.1"})), 129 newIPAddress("2001:db8:a:b::10", newService("test-svc", []string{"2001:db8:a:b::10"})), 130 }, 131 cidrs: []*networkingv1alpha1.ServiceCIDR{ 132 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 133 newServiceCIDR("custom", "192.168.0.0/24", "2001:db8:a:b::/64"), 134 }, 135 expectedIPs: []string{"192.168.0.1", "2001:db8:a:b::10"}, 136 actions: [][]string{}, 137 events: []string{}, 138 }, 139 // these two cases simulate migrating from bitmaps to IPAddress objects 140 { 141 name: "create IPAddress single stack", 142 svcs: []*v1.Service{newService("test-svc", []string{"10.0.1.1"})}, 143 cidrs: []*networkingv1alpha1.ServiceCIDR{ 144 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 145 }, 146 expectedIPs: []string{"10.0.1.1"}, 147 actions: [][]string{{"create", "ipaddresses"}}, 148 events: []string{"Warning ClusterIPNotAllocated Cluster IP [IPv4]: 10.0.1.1 is not allocated; repairing"}, 149 }, 150 { 151 name: "create IPAddresses dual stack", 152 svcs: []*v1.Service{newService("test-svc", []string{"10.0.1.1", "2001:db8::10"})}, 153 cidrs: []*networkingv1alpha1.ServiceCIDR{ 154 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 155 }, 156 expectedIPs: []string{"10.0.1.1", "2001:db8::10"}, 157 actions: [][]string{{"create", "ipaddresses"}, {"create", "ipaddresses"}}, 158 events: []string{ 159 "Warning ClusterIPNotAllocated Cluster IP [IPv4]: 10.0.1.1 is not allocated; repairing", 160 "Warning ClusterIPNotAllocated Cluster IP [IPv6]: 2001:db8::10 is not allocated; repairing", 161 }, 162 }, 163 { 164 name: "create IPAddress single stack from secondary", 165 svcs: []*v1.Service{newService("test-svc", []string{"192.168.1.1"})}, 166 cidrs: []*networkingv1alpha1.ServiceCIDR{ 167 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 168 newServiceCIDR("custom", "192.168.1.0/24", ""), 169 }, 170 expectedIPs: []string{"192.168.1.1"}, 171 actions: [][]string{{"create", "ipaddresses"}}, 172 events: []string{"Warning ClusterIPNotAllocated Cluster IP [IPv4]: 192.168.1.1 is not allocated; repairing"}, 173 }, 174 { 175 name: "reconcile IPAddress single stack wrong reference", 176 svcs: []*v1.Service{newService("test-svc", []string{"10.0.1.1"})}, 177 ipAddresses: []*networkingv1alpha1.IPAddress{ 178 newIPAddress("10.0.1.1", newService("test-svc2", []string{"10.0.1.1"})), 179 }, 180 cidrs: []*networkingv1alpha1.ServiceCIDR{ 181 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 182 }, 183 expectedIPs: []string{"10.0.1.1"}, 184 actions: [][]string{{"delete", "ipaddresses"}, {"create", "ipaddresses"}}, 185 events: []string{"Warning ClusterIPNotAllocated the ClusterIP [IPv4]: 10.0.1.1 for Service bar/test-svc has a wrong reference; repairing"}, 186 }, 187 { 188 name: "reconcile IPAddresses dual stack", 189 svcs: []*v1.Service{newService("test-svc", []string{"10.0.1.1", "2001:db8::10"})}, 190 ipAddresses: []*networkingv1alpha1.IPAddress{ 191 newIPAddress("10.0.1.1", newService("test-svc2", []string{"10.0.1.1"})), 192 newIPAddress("2001:db8::10", newService("test-svc2", []string{"2001:db8::10"})), 193 }, 194 cidrs: []*networkingv1alpha1.ServiceCIDR{ 195 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 196 }, 197 expectedIPs: []string{"10.0.1.1", "2001:db8::10"}, 198 actions: [][]string{{"delete", "ipaddresses"}, {"create", "ipaddresses"}, {"delete", "ipaddresses"}, {"create", "ipaddresses"}}, 199 events: []string{ 200 "Warning ClusterIPNotAllocated the ClusterIP [IPv4]: 10.0.1.1 for Service bar/test-svc has a wrong reference; repairing", 201 "Warning ClusterIPNotAllocated the ClusterIP [IPv6]: 2001:db8::10 for Service bar/test-svc has a wrong reference; repairing", 202 }, 203 }, 204 { 205 name: "one IP out of range", 206 svcs: []*v1.Service{newService("test-svc", []string{"192.168.1.1", "2001:db8::10"})}, 207 ipAddresses: []*networkingv1alpha1.IPAddress{ 208 newIPAddress("192.168.1.1", newService("test-svc", []string{"192.168.1.1"})), 209 newIPAddress("2001:db8::10", newService("test-svc", []string{"2001:db8::10"})), 210 }, 211 cidrs: []*networkingv1alpha1.ServiceCIDR{ 212 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 213 }, 214 expectedIPs: []string{"2001:db8::10"}, 215 actions: [][]string{}, 216 events: []string{"Warning ClusterIPOutOfRange Cluster IP [IPv4]: 192.168.1.1 is not within any configured Service CIDR; please recreate service"}, 217 }, 218 { 219 name: "one IP orphan", 220 ipAddresses: []*networkingv1alpha1.IPAddress{ 221 newIPAddress("10.0.1.1", newService("test-svc", []string{"10.0.1.1"})), 222 }, 223 cidrs: []*networkingv1alpha1.ServiceCIDR{ 224 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 225 }, 226 actions: [][]string{{"delete", "ipaddresses"}}, 227 events: []string{"Warning IPAddressNotAllocated IPAddress: 10.0.1.1 for Service bar/test-svc appears to have leaked: cleaning up"}, 228 }, 229 { 230 name: "one IP out of range matching the network address", 231 svcs: []*v1.Service{newService("test-svc", []string{"10.0.0.0"})}, 232 ipAddresses: []*networkingv1alpha1.IPAddress{ 233 newIPAddress("10.0.0.0", newService("test-svc", []string{"10.0.0.0"})), 234 }, 235 cidrs: []*networkingv1alpha1.ServiceCIDR{ 236 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 237 }, 238 expectedIPs: []string{"10.0.0.0"}, 239 actions: [][]string{}, 240 events: []string{"Warning ClusterIPOutOfRange Cluster IP [IPv4]: 10.0.0.0 is not within any configured Service CIDR; please recreate service"}, 241 }, 242 { 243 name: "one IP out of range matching the broadcast address", 244 svcs: []*v1.Service{newService("test-svc", []string{"10.0.255.255"})}, 245 ipAddresses: []*networkingv1alpha1.IPAddress{ 246 newIPAddress("10.0.255.255", newService("test-svc", []string{"10.0.255.255"})), 247 }, 248 cidrs: []*networkingv1alpha1.ServiceCIDR{ 249 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 250 }, 251 expectedIPs: []string{"10.0.255.255"}, 252 actions: [][]string{}, 253 events: []string{"Warning ClusterIPOutOfRange Cluster IP [IPv4]: 10.0.255.255 is not within any configured Service CIDR; please recreate service"}, 254 }, 255 { 256 name: "one IPv6 out of range matching the subnet address", 257 svcs: []*v1.Service{newService("test-svc", []string{"2001:db8::"})}, 258 ipAddresses: []*networkingv1alpha1.IPAddress{ 259 newIPAddress("2001:db8::", newService("test-svc", []string{"2001:db8::"})), 260 }, 261 cidrs: []*networkingv1alpha1.ServiceCIDR{ 262 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 263 }, 264 expectedIPs: []string{"2001:db8::"}, 265 actions: [][]string{}, 266 events: []string{"Warning ClusterIPOutOfRange Cluster IP [IPv6]: 2001:db8:: is not within any configured Service CIDR; please recreate service"}, 267 }, 268 { 269 name: "one IPv6 matching the broadcast address", 270 svcs: []*v1.Service{newService("test-svc", []string{"2001:db8::ffff:ffff:ffff:ffff"})}, 271 ipAddresses: []*networkingv1alpha1.IPAddress{ 272 newIPAddress("2001:db8::ffff:ffff:ffff:ffff", newService("test-svc", []string{"2001:db8::ffff:ffff:ffff:ffff"})), 273 }, 274 cidrs: []*networkingv1alpha1.ServiceCIDR{ 275 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 276 }, 277 expectedIPs: []string{"2001:db8::ffff:ffff:ffff:ffff"}, 278 }, 279 { 280 name: "one IP orphan matching the broadcast address", 281 ipAddresses: []*networkingv1alpha1.IPAddress{ 282 newIPAddress("10.0.255.255", newService("test-svc", []string{"10.0.255.255"})), 283 }, 284 cidrs: []*networkingv1alpha1.ServiceCIDR{ 285 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 286 }, 287 actions: [][]string{{"delete", "ipaddresses"}}, 288 events: []string{"Warning IPAddressNotAllocated IPAddress: 10.0.255.255 for Service bar/test-svc appears to have leaked: cleaning up"}, 289 }, 290 { 291 name: "Two IPAddresses referencing the same service", 292 svcs: []*v1.Service{newService("test-svc", []string{"10.0.1.1"})}, 293 ipAddresses: []*networkingv1alpha1.IPAddress{ 294 newIPAddress("10.0.1.1", newService("test-svc", []string{"10.0.1.1"})), 295 newIPAddress("10.0.1.2", newService("test-svc", []string{"10.0.1.1"})), 296 }, 297 cidrs: []*networkingv1alpha1.ServiceCIDR{ 298 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 299 }, 300 actions: [][]string{{"delete", "ipaddresses"}}, 301 events: []string{"Warning IPAddressWrongReference IPAddress: 10.0.1.2 for Service bar/test-svc has a wrong reference; cleaning up"}, 302 }, 303 { 304 name: "Two Services with same ClusterIP", 305 svcs: []*v1.Service{ 306 newService("test-svc", []string{"10.0.1.1"}), 307 newService("test-svc2", []string{"10.0.1.1"}), 308 }, 309 ipAddresses: []*networkingv1alpha1.IPAddress{ 310 newIPAddress("10.0.1.1", newService("test-svc2", []string{"10.0.1.1"})), 311 }, 312 cidrs: []*networkingv1alpha1.ServiceCIDR{ 313 newServiceCIDR("kubernetes", serviceCIDRv4, serviceCIDRv6), 314 }, 315 events: []string{"Warning ClusterIPAlreadyAllocated Cluster IP [IPv4]:10.0.1.1 was assigned to multiple services; please recreate service"}, 316 }, 317 } 318 319 for _, test := range tests { 320 t.Run(test.name, func(t *testing.T) { 321 322 c, r := newFakeRepair() 323 // add cidrs 324 for _, cidr := range test.cidrs { 325 err := r.serviceCIDRStore.Add(cidr) 326 if err != nil { 327 t.Errorf("Unexpected error trying to add Service %v object: %v", cidr, err) 328 } 329 } 330 err := r.syncCIDRs() 331 if err != nil { 332 t.Fatal(err) 333 } 334 // override for testing 335 r.servicesSynced = func() bool { return true } 336 r.ipAddressSynced = func() bool { return true } 337 r.serviceCIDRSynced = func() bool { return true } 338 recorder := events.NewFakeRecorder(100) 339 r.recorder = recorder 340 for _, svc := range test.svcs { 341 err := r.serviceStore.Add(svc) 342 if err != nil { 343 t.Errorf("Unexpected error trying to add Service %v object: %v", svc, err) 344 } 345 } 346 347 for _, ip := range test.ipAddresses { 348 ip.CreationTimestamp = metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC) 349 err := r.ipAddressStore.Add(ip) 350 if err != nil { 351 t.Errorf("Unexpected error trying to add IPAddress %s object: %v", ip, err) 352 } 353 } 354 355 err = r.runOnce() 356 if err != nil { 357 t.Fatal(err) 358 } 359 360 for _, ip := range test.expectedIPs { 361 _, err := r.ipAddressLister.Get(ip) 362 if err != nil { 363 t.Errorf("Unexpected error trying to get IPAddress %s object: %v", ip, err) 364 } 365 } 366 367 expectAction(t, c.Actions(), test.actions) 368 expectEvents(t, recorder.Events, test.events) 369 }) 370 } 371 372 } 373 374 func TestRepairIPAddress_syncIPAddress(t *testing.T) { 375 tests := []struct { 376 name string 377 ip *networkingv1alpha1.IPAddress 378 actions [][]string // verb and resource 379 wantErr bool 380 }{ 381 { 382 name: "correct ipv4 address", 383 ip: &networkingv1alpha1.IPAddress{ 384 ObjectMeta: metav1.ObjectMeta{ 385 Name: "10.0.1.1", 386 Labels: map[string]string{ 387 networkingv1alpha1.LabelIPAddressFamily: string(v1.IPv4Protocol), 388 networkingv1alpha1.LabelManagedBy: ipallocator.ControllerName, 389 }, 390 CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), 391 }, 392 Spec: networkingv1alpha1.IPAddressSpec{ 393 ParentRef: &networkingv1alpha1.ParentReference{ 394 Group: "", 395 Resource: "services", 396 Name: "foo", 397 Namespace: "bar", 398 }, 399 }, 400 }, 401 }, 402 { 403 name: "correct ipv6 address", 404 ip: &networkingv1alpha1.IPAddress{ 405 ObjectMeta: metav1.ObjectMeta{ 406 Name: "2001:db8::11", 407 Labels: map[string]string{ 408 networkingv1alpha1.LabelIPAddressFamily: string(v1.IPv6Protocol), 409 networkingv1alpha1.LabelManagedBy: ipallocator.ControllerName, 410 }, 411 CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), 412 }, 413 Spec: networkingv1alpha1.IPAddressSpec{ 414 ParentRef: &networkingv1alpha1.ParentReference{ 415 Group: "", 416 Resource: "services", 417 Name: "foo", 418 Namespace: "bar", 419 }, 420 }, 421 }, 422 }, 423 { 424 name: "not managed by this controller", 425 ip: &networkingv1alpha1.IPAddress{ 426 ObjectMeta: metav1.ObjectMeta{ 427 Name: "2001:db8::11", 428 Labels: map[string]string{ 429 networkingv1alpha1.LabelIPAddressFamily: string(v1.IPv6Protocol), 430 networkingv1alpha1.LabelManagedBy: "controller-foo", 431 }, 432 CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), 433 }, 434 Spec: networkingv1alpha1.IPAddressSpec{ 435 ParentRef: &networkingv1alpha1.ParentReference{ 436 Group: "networking.gateway.k8s.io", 437 Resource: "gateway", 438 Name: "foo", 439 Namespace: "bar", 440 }, 441 }, 442 }, 443 }, 444 { 445 name: "out of range", 446 ip: &networkingv1alpha1.IPAddress{ 447 ObjectMeta: metav1.ObjectMeta{ 448 Name: "fd00:db8::11", 449 Labels: map[string]string{ 450 networkingv1alpha1.LabelIPAddressFamily: string(v1.IPv6Protocol), 451 networkingv1alpha1.LabelManagedBy: ipallocator.ControllerName, 452 }, 453 CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), 454 }, 455 Spec: networkingv1alpha1.IPAddressSpec{ 456 ParentRef: &networkingv1alpha1.ParentReference{ 457 Group: "", 458 Resource: "services", 459 Name: "foo", 460 Namespace: "bar", 461 }, 462 }, 463 }, 464 }, 465 { 466 name: "leaked ip", 467 ip: &networkingv1alpha1.IPAddress{ 468 ObjectMeta: metav1.ObjectMeta{ 469 Name: "10.0.1.1", 470 Labels: map[string]string{ 471 networkingv1alpha1.LabelIPAddressFamily: string(v1.IPv6Protocol), 472 networkingv1alpha1.LabelManagedBy: ipallocator.ControllerName, 473 }, 474 CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), 475 }, 476 Spec: networkingv1alpha1.IPAddressSpec{ 477 ParentRef: &networkingv1alpha1.ParentReference{ 478 Group: "", 479 Resource: "services", 480 Name: "noexist", 481 Namespace: "bar", 482 }, 483 }, 484 }, 485 actions: [][]string{{"delete", "ipaddresses"}}, 486 }, 487 } 488 for _, tt := range tests { 489 t.Run(tt.name, func(t *testing.T) { 490 c, r := newFakeRepair() 491 err := r.ipAddressStore.Add(tt.ip) 492 if err != nil { 493 t.Fatal(err) 494 } 495 err = r.serviceStore.Add(newService("foo", []string{tt.ip.Name})) 496 if err != nil { 497 t.Fatal(err) 498 } 499 500 // override for testing 501 r.servicesSynced = func() bool { return true } 502 r.ipAddressSynced = func() bool { return true } 503 recorder := events.NewFakeRecorder(100) 504 r.recorder = recorder 505 if err := r.syncIPAddress(tt.ip.Name); (err != nil) != tt.wantErr { 506 t.Errorf("RepairIPAddress.syncIPAddress() error = %v, wantErr %v", err, tt.wantErr) 507 } 508 expectAction(t, c.Actions(), tt.actions) 509 510 }) 511 } 512 } 513 514 func newService(name string, ips []string) *v1.Service { 515 if len(ips) == 0 { 516 return nil 517 } 518 svc := &v1.Service{ 519 ObjectMeta: metav1.ObjectMeta{Namespace: "bar", Name: name}, 520 Spec: v1.ServiceSpec{ 521 ClusterIP: ips[0], 522 ClusterIPs: ips, 523 Type: v1.ServiceTypeClusterIP, 524 }, 525 } 526 return svc 527 } 528 529 func newServiceCIDR(name, primary, secondary string) *networkingv1alpha1.ServiceCIDR { 530 serviceCIDR := &networkingv1alpha1.ServiceCIDR{ 531 ObjectMeta: metav1.ObjectMeta{ 532 Name: name, 533 }, 534 Spec: networkingv1alpha1.ServiceCIDRSpec{}, 535 } 536 serviceCIDR.Spec.CIDRs = append(serviceCIDR.Spec.CIDRs, primary) 537 if secondary != "" { 538 serviceCIDR.Spec.CIDRs = append(serviceCIDR.Spec.CIDRs, secondary) 539 } 540 return serviceCIDR 541 } 542 543 func expectAction(t *testing.T, actions []k8stesting.Action, expected [][]string) { 544 t.Helper() 545 if len(actions) != len(expected) { 546 t.Fatalf("Expected at least %d actions, got %d \ndiff: %v", len(expected), len(actions), cmp.Diff(expected, actions)) 547 } 548 549 for i, action := range actions { 550 verb := expected[i][0] 551 if action.GetVerb() != verb { 552 t.Errorf("Expected action %d verb to be %s, got %s", i, verb, action.GetVerb()) 553 } 554 resource := expected[i][1] 555 if action.GetResource().Resource != resource { 556 t.Errorf("Expected action %d resource to be %s, got %s", i, resource, action.GetResource().Resource) 557 } 558 } 559 } 560 561 func expectEvents(t *testing.T, actual <-chan string, expected []string) { 562 t.Helper() 563 c := time.After(wait.ForeverTestTimeout) 564 for _, e := range expected { 565 select { 566 case a := <-actual: 567 if e != a { 568 t.Errorf("Expected event %q, got %q", e, a) 569 return 570 } 571 case <-c: 572 t.Errorf("Expected event %q, got nothing", e) 573 // continue iterating to print all expected events 574 } 575 } 576 for { 577 select { 578 case a := <-actual: 579 t.Errorf("Unexpected event: %q", a) 580 default: 581 return // No more events, as expected. 582 } 583 } 584 }