k8s.io/kubernetes@v1.29.3/pkg/registry/core/service/ipallocator/cidrallocator_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 ipallocator 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 "time" 24 25 networkingv1alpha1 "k8s.io/api/networking/v1alpha1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/apimachinery/pkg/util/sets" 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 netutils "k8s.io/utils/net" 34 ) 35 36 func newTestMetaAllocator() (*MetaAllocator, error) { 37 client := fake.NewSimpleClientset() 38 39 informerFactory := informers.NewSharedInformerFactory(client, 0*time.Second) 40 serviceCIDRInformer := informerFactory.Networking().V1alpha1().ServiceCIDRs() 41 serviceCIDRStore := serviceCIDRInformer.Informer().GetIndexer() 42 serviceCIDRInformer.Informer().HasSynced() 43 ipInformer := informerFactory.Networking().V1alpha1().IPAddresses() 44 ipStore := ipInformer.Informer().GetIndexer() 45 46 client.PrependReactor("create", "servicecidrs", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) { 47 cidr := action.(k8stesting.CreateAction).GetObject().(*networkingv1alpha1.ServiceCIDR) 48 _, exists, err := serviceCIDRStore.GetByKey(cidr.Name) 49 if exists && err != nil { 50 return false, nil, fmt.Errorf("cidr already exist") 51 } 52 cidr.Generation = 1 53 err = serviceCIDRStore.Add(cidr) 54 return false, cidr, err 55 })) 56 client.PrependReactor("delete", "servicecidrs", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) { 57 name := action.(k8stesting.DeleteAction).GetName() 58 obj, exists, err := serviceCIDRStore.GetByKey(name) 59 cidr := &networkingv1alpha1.ServiceCIDR{} 60 if exists && err == nil { 61 cidr = obj.(*networkingv1alpha1.ServiceCIDR) 62 err = serviceCIDRStore.Delete(cidr) 63 } 64 return false, cidr, err 65 })) 66 67 client.PrependReactor("create", "ipaddresses", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) { 68 ip := action.(k8stesting.CreateAction).GetObject().(*networkingv1alpha1.IPAddress) 69 _, exists, err := ipStore.GetByKey(ip.Name) 70 if exists && err != nil { 71 return false, nil, fmt.Errorf("ip already exist") 72 } 73 ip.Generation = 1 74 err = ipStore.Add(ip) 75 return false, ip, err 76 })) 77 client.PrependReactor("delete", "ipaddresses", k8stesting.ReactionFunc(func(action k8stesting.Action) (bool, runtime.Object, error) { 78 name := action.(k8stesting.DeleteAction).GetName() 79 obj, exists, err := ipStore.GetByKey(name) 80 ip := &networkingv1alpha1.IPAddress{} 81 if exists && err == nil { 82 ip = obj.(*networkingv1alpha1.IPAddress) 83 err = ipStore.Delete(ip) 84 } 85 return false, ip, err 86 })) 87 88 c, err := NewMetaAllocator(client.NetworkingV1alpha1(), serviceCIDRInformer, ipInformer, false) 89 if err != nil { 90 return nil, err 91 } 92 // we can not force the state of the informers to be synced without racing 93 // so we run our worker here 94 go wait.Until(c.runWorker, time.Second, c.internalStopCh) 95 return c, nil 96 } 97 98 func TestCIDRAllocateMultiple(t *testing.T) { 99 r, err := newTestMetaAllocator() 100 if err != nil { 101 t.Fatal(err) 102 } 103 defer r.Destroy() 104 105 if f := r.Free(); f != 0 { 106 t.Errorf("free: %d", f) 107 } 108 if _, err := r.AllocateNext(); err == nil { 109 t.Error(err) 110 } 111 112 cidr := newServiceCIDR("test", "192.168.0.0/28") 113 _, err = r.client.ServiceCIDRs().Create(context.Background(), cidr, metav1.CreateOptions{}) 114 if err != nil { 115 t.Fatal(err) 116 } 117 r.addServiceCIDR(cidr) 118 // wait for the cidr to be processed and set the informer synced 119 err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { 120 allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.1")) 121 if err != nil { 122 t.Logf("unexpected error %v", err) 123 return false, nil 124 } 125 allocator.ipAddressSynced = func() bool { return true } 126 return allocator.ready.Load(), nil 127 }) 128 if err != nil { 129 t.Fatal(err) 130 } 131 found := sets.NewString() 132 count := 0 133 for r.Free() > 0 { 134 ip, err := r.AllocateNext() 135 if err != nil { 136 t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err) 137 } 138 count++ 139 if found.Has(ip.String()) { 140 t.Fatalf("allocated %s twice: %d", ip, count) 141 } 142 found.Insert(ip.String()) 143 } 144 if count != 14 { 145 t.Fatalf("expected 14 IPs got %d", count) 146 } 147 if _, err := r.AllocateNext(); err == nil { 148 t.Fatal(err) 149 } 150 151 cidr2 := newServiceCIDR("test2", "10.0.0.0/28") 152 _, err = r.client.ServiceCIDRs().Create(context.Background(), cidr2, metav1.CreateOptions{}) 153 if err != nil { 154 t.Fatal(err) 155 } 156 r.addServiceCIDR(cidr2) 157 // wait for the cidr to be processed 158 err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { 159 allocator, err := r.getAllocator(netutils.ParseIPSloppy("10.0.0.11")) 160 if err != nil { 161 return false, nil 162 } 163 allocator.ipAddressSynced = func() bool { return true } 164 return allocator.ready.Load(), nil 165 }) 166 if err != nil { 167 t.Fatal(err) 168 } 169 // allocate one IP from the new allocator 170 err = r.Allocate(netutils.ParseIPSloppy("10.0.0.11")) 171 if err != nil { 172 t.Fatalf("error allocating IP 10.0.0.11 from new allocator: %v", err) 173 } 174 count++ 175 for r.Free() > 0 { 176 ip, err := r.AllocateNext() 177 if err != nil { 178 t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err) 179 } 180 count++ 181 if found.Has(ip.String()) { 182 t.Fatalf("allocated %s twice: %d", ip, count) 183 } 184 found.Insert(ip.String()) 185 } 186 if count != 28 { 187 t.Fatalf("expected 28 IPs got %d", count) 188 } 189 if _, err := r.AllocateNext(); err == nil { 190 t.Fatal(err) 191 } 192 193 } 194 195 func TestCIDRAllocateShadow(t *testing.T) { 196 r, err := newTestMetaAllocator() 197 if err != nil { 198 t.Fatal(err) 199 } 200 defer r.Destroy() 201 202 if f := r.Free(); f != 0 { 203 t.Errorf("free: %d", f) 204 } 205 if _, err := r.AllocateNext(); err == nil { 206 t.Error(err) 207 } 208 209 cidr := newServiceCIDR("test", "192.168.1.0/24") 210 _, err = r.client.ServiceCIDRs().Create(context.Background(), cidr, metav1.CreateOptions{}) 211 if err != nil { 212 t.Fatal(err) 213 } 214 r.addServiceCIDR(cidr) 215 // wait for the cidr to be processed and set the informer synced 216 err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { 217 allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.1.0")) 218 if err != nil { 219 return false, nil 220 } 221 allocator.ipAddressSynced = func() bool { return true } 222 return allocator.ready.Load(), nil 223 }) 224 if err != nil { 225 t.Fatal(err) 226 } 227 // allocate one IP from the new allocator 228 err = r.Allocate(netutils.ParseIPSloppy("192.168.1.0")) 229 if err == nil { 230 t.Fatalf("unexpected allocation for IP 192.168.1.0") 231 } 232 233 if f := r.Used(); f != 0 { 234 t.Errorf("used: %d", f) 235 } 236 237 cidr2 := newServiceCIDR("test2", "192.168.0.0/16") 238 _, err = r.client.ServiceCIDRs().Create(context.Background(), cidr2, metav1.CreateOptions{}) 239 if err != nil { 240 t.Fatal(err) 241 } 242 r.addServiceCIDR(cidr2) 243 // wait for the cidr to be processed 244 err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { 245 allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.0")) 246 if err != nil { 247 return false, nil 248 } 249 allocator.ipAddressSynced = func() bool { return true } 250 return allocator.ready.Load(), nil 251 }) 252 if err != nil { 253 t.Fatal(err) 254 } 255 // allocate one IP from the new allocator 256 err = r.Allocate(netutils.ParseIPSloppy("192.168.1.0")) 257 if err != nil { 258 t.Fatalf("error allocating IP 192.168.1.0 from new allocator: %v", err) 259 } 260 261 if f := r.Used(); f != 1 { 262 t.Errorf("used: %d", f) 263 } 264 265 } 266 267 func TestCIDRAllocateGrow(t *testing.T) { 268 r, err := newTestMetaAllocator() 269 if err != nil { 270 t.Fatal(err) 271 } 272 defer r.Destroy() 273 274 if f := r.Free(); f != 0 { 275 t.Errorf("free: %d", f) 276 } 277 if _, err := r.AllocateNext(); err == nil { 278 t.Error(err) 279 } 280 281 cidr := newServiceCIDR("test", "192.168.0.0/28") 282 _, err = r.client.ServiceCIDRs().Create(context.Background(), cidr, metav1.CreateOptions{}) 283 if err != nil { 284 t.Fatal(err) 285 } 286 r.addServiceCIDR(cidr) 287 // wait for the cidr to be processed 288 err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { 289 allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.1")) 290 if err != nil { 291 return false, nil 292 } 293 allocator.ipAddressSynced = func() bool { return true } 294 return allocator.ready.Load(), nil 295 }) 296 if err != nil { 297 t.Fatal(err) 298 } 299 found := sets.NewString() 300 count := 0 301 for r.Free() > 0 { 302 ip, err := r.AllocateNext() 303 if err != nil { 304 t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err) 305 } 306 count++ 307 if found.Has(ip.String()) { 308 t.Fatalf("allocated %s twice: %d", ip, count) 309 } 310 found.Insert(ip.String()) 311 } 312 if count != 14 { 313 t.Fatalf("expected 14 IPs got %d", count) 314 } 315 if _, err := r.AllocateNext(); err == nil { 316 t.Fatal(err) 317 } 318 319 cidr2 := newServiceCIDR("test2", "192.168.0.0/24") 320 _, err = r.client.ServiceCIDRs().Create(context.Background(), cidr2, metav1.CreateOptions{}) 321 if err != nil { 322 t.Fatal(err) 323 } 324 r.addServiceCIDR(cidr2) 325 // wait for the cidr to be processed 326 err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { 327 allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.253")) 328 if err != nil { 329 return false, nil 330 } 331 allocator.ipAddressSynced = func() bool { return true } 332 return allocator.ready.Load(), nil 333 }) 334 if err != nil { 335 t.Fatal(err) 336 } 337 338 for r.Free() > 0 { 339 ip, err := r.AllocateNext() 340 if err != nil { 341 t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err) 342 } 343 count++ 344 if found.Has(ip.String()) { 345 t.Fatalf("allocated %s twice: %d", ip, count) 346 } 347 found.Insert(ip.String()) 348 } 349 if count != 254 { 350 t.Fatalf("expected 254 IPs got %d", count) 351 } 352 if _, err := r.AllocateNext(); err == nil { 353 t.Fatal(err) 354 } 355 356 } 357 358 func TestCIDRAllocateShrink(t *testing.T) { 359 r, err := newTestMetaAllocator() 360 if err != nil { 361 t.Fatal(err) 362 } 363 defer r.Destroy() 364 365 if f := r.Free(); f != 0 { 366 t.Errorf("free: %d", f) 367 } 368 if _, err := r.AllocateNext(); err == nil { 369 t.Error(err) 370 } 371 372 cidr := newServiceCIDR("test", "192.168.0.0/24") 373 _, err = r.client.ServiceCIDRs().Create(context.Background(), cidr, metav1.CreateOptions{}) 374 if err != nil { 375 t.Fatal(err) 376 } 377 r.addServiceCIDR(cidr) 378 // wait for the cidr to be processed 379 err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { 380 allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.1")) 381 if err != nil { 382 return false, nil 383 } 384 allocator.ipAddressSynced = func() bool { return true } 385 return allocator.ready.Load(), nil 386 }) 387 if err != nil { 388 t.Fatal(err) 389 } 390 found := sets.NewString() 391 count := 0 392 for r.Free() > 0 { 393 ip, err := r.AllocateNext() 394 if err != nil { 395 t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err) 396 } 397 count++ 398 if found.Has(ip.String()) { 399 t.Fatalf("allocated %s twice: %d", ip, count) 400 } 401 found.Insert(ip.String()) 402 } 403 if count != 254 { 404 t.Fatalf("expected 254 IPs got %d", count) 405 } 406 if _, err := r.AllocateNext(); err == nil { 407 t.Fatal(err) 408 } 409 for _, ip := range found.List() { 410 err = r.Release(netutils.ParseIPSloppy(ip)) 411 if err != nil { 412 t.Fatalf("unexpected error releasing ip %s", err) 413 } 414 } 415 if r.Used() > 0 { 416 t.Fatalf("expected allocator to be empty, got %d", r.Free()) 417 } 418 cidr2 := newServiceCIDR("cidr2", "192.168.0.0/28") 419 _, err = r.client.ServiceCIDRs().Create(context.Background(), cidr2, metav1.CreateOptions{}) 420 if err != nil { 421 t.Fatal(err) 422 } 423 r.addServiceCIDR(cidr2) 424 err = r.client.ServiceCIDRs().Delete(context.Background(), cidr.Name, metav1.DeleteOptions{}) 425 if err != nil { 426 t.Fatal(err) 427 } 428 r.deleteServiceCIDR(cidr) 429 430 // wait for the cidr to be processed (delete ServiceCIDR) 431 err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { 432 _, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.253")) 433 if err != nil { 434 return true, nil 435 } 436 437 return false, nil 438 }) 439 if err != nil { 440 t.Fatal(err) 441 } 442 // wait for the cidr to be processed (create ServiceCIDR) 443 err = wait.PollUntilContextTimeout(context.Background(), 100*time.Millisecond, 5*time.Second, true, func(ctx context.Context) (bool, error) { 444 allocator, err := r.getAllocator(netutils.ParseIPSloppy("192.168.0.1")) 445 if err != nil { 446 return false, nil 447 } 448 allocator.ipAddressSynced = func() bool { return true } 449 return allocator.ready.Load(), nil 450 }) 451 if err != nil { 452 t.Fatal(err) 453 } 454 count = 0 455 for r.Free() > 0 { 456 _, err := r.AllocateNext() 457 if err != nil { 458 t.Fatalf("error @ free: %d count: %d: %v", r.Free(), count, err) 459 } 460 count++ 461 } 462 if count != 14 { 463 t.Fatalf("expected 14 IPs got %d", count) 464 } 465 if _, err := r.AllocateNext(); err == nil { 466 t.Fatal(err) 467 } 468 469 } 470 471 // TODO: add IPv6 and dual stack test cases 472 func newServiceCIDR(name, cidr string) *networkingv1alpha1.ServiceCIDR { 473 return &networkingv1alpha1.ServiceCIDR{ 474 ObjectMeta: metav1.ObjectMeta{ 475 Name: name, 476 }, 477 Spec: networkingv1alpha1.ServiceCIDRSpec{ 478 CIDRs: []string{cidr}, 479 }, 480 Status: networkingv1alpha1.ServiceCIDRStatus{ 481 Conditions: []metav1.Condition{ 482 { 483 Type: string(networkingv1alpha1.ServiceCIDRConditionReady), 484 Status: metav1.ConditionTrue, 485 }, 486 }, 487 }, 488 } 489 }