k8s.io/kubernetes@v1.29.3/pkg/registry/core/service/ipallocator/bitmap_test.go (about) 1 /* 2 Copyright 2015 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 "fmt" 21 "net" 22 "testing" 23 24 "k8s.io/apimachinery/pkg/util/sets" 25 "k8s.io/component-base/metrics/testutil" 26 api "k8s.io/kubernetes/pkg/apis/core" 27 netutils "k8s.io/utils/net" 28 ) 29 30 func TestAllocate(t *testing.T) { 31 testCases := []struct { 32 name string 33 cidr string 34 family api.IPFamily 35 free int 36 released string 37 outOfRange []string 38 alreadyAllocated string 39 }{ 40 { 41 name: "IPv4", 42 cidr: "192.168.1.0/24", 43 family: api.IPv4Protocol, 44 free: 254, 45 released: "192.168.1.5", 46 outOfRange: []string{ 47 "192.168.0.1", // not in 192.168.1.0/24 48 "192.168.1.0", // reserved (base address) 49 "192.168.1.255", // reserved (broadcast address) 50 "192.168.2.2", // not in 192.168.1.0/24 51 }, 52 alreadyAllocated: "192.168.1.1", 53 }, 54 { 55 name: "IPv4 large", 56 cidr: "10.0.0.0/15", 57 family: api.IPv4Protocol, 58 free: 131070, 59 released: "10.0.0.5", 60 outOfRange: []string{ 61 "10.0.0.0", // reserved (base address) 62 "10.15.255.255", // reserved (broadcast address) 63 "10.255.255.2", // not in range 64 }, 65 alreadyAllocated: "10.0.0.1", 66 }, 67 { 68 name: "IPv6", 69 cidr: "2001:db8:1::/48", 70 family: api.IPv6Protocol, 71 free: 65535, 72 released: "2001:db8:1::5", 73 outOfRange: []string{ 74 "2001:db8::1", // not in 2001:db8:1::/48 75 "2001:db8:1::", // reserved (base address) 76 "2001:db8:1::1:0", // not in the low 16 bits of 2001:db8:1::/48 77 "2001:db8:2::2", // not in 2001:db8:1::/48 78 }, 79 alreadyAllocated: "2001:db8:1::1", 80 }, 81 } 82 for _, tc := range testCases { 83 t.Run(tc.name, func(t *testing.T) { 84 _, cidr, err := netutils.ParseCIDRSloppy(tc.cidr) 85 if err != nil { 86 t.Fatal(err) 87 } 88 r, err := NewInMemory(cidr) 89 if err != nil { 90 t.Fatal(err) 91 } 92 if f := r.Free(); f != tc.free { 93 t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, tc.free, f) 94 } 95 96 rCIDR := r.CIDR() 97 if rCIDR.String() != tc.cidr { 98 t.Errorf("[%s] wrong CIDR: expected %v, got %v", tc.name, tc.cidr, rCIDR.String()) 99 } 100 101 if r.IPFamily() != tc.family { 102 t.Errorf("[%s] wrong IP family: expected %v, got %v", tc.name, tc.family, r.IPFamily()) 103 } 104 105 if f := r.Used(); f != 0 { 106 t.Errorf("[%s]: wrong used: expected %d, got %d", tc.name, 0, f) 107 } 108 found := sets.NewString() 109 count := 0 110 for r.Free() > 0 { 111 ip, err := r.AllocateNext() 112 if err != nil { 113 t.Fatalf("[%s] error @ %d: %v", tc.name, count, err) 114 } 115 count++ 116 if !cidr.Contains(ip) { 117 t.Fatalf("[%s] allocated %s which is outside of %s", tc.name, ip, cidr) 118 } 119 if found.Has(ip.String()) { 120 t.Fatalf("[%s] allocated %s twice @ %d", tc.name, ip, count) 121 } 122 found.Insert(ip.String()) 123 } 124 if _, err := r.AllocateNext(); err != ErrFull { 125 t.Fatal(err) 126 } 127 128 released := netutils.ParseIPSloppy(tc.released) 129 if err := r.Release(released); err != nil { 130 t.Fatal(err) 131 } 132 if f := r.Free(); f != 1 { 133 t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, 1, f) 134 } 135 if f := r.Used(); f != (tc.free - 1) { 136 t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, tc.free-1, f) 137 } 138 ip, err := r.AllocateNext() 139 if err != nil { 140 t.Fatal(err) 141 } 142 if !released.Equal(ip) { 143 t.Errorf("[%s] unexpected %s : %s", tc.name, ip, released) 144 } 145 146 if err := r.Release(released); err != nil { 147 t.Fatal(err) 148 } 149 for _, outOfRange := range tc.outOfRange { 150 err = r.Allocate(netutils.ParseIPSloppy(outOfRange)) 151 if _, ok := err.(*ErrNotInRange); !ok { 152 t.Fatal(err) 153 } 154 } 155 if err := r.Allocate(netutils.ParseIPSloppy(tc.alreadyAllocated)); err != ErrAllocated { 156 t.Fatal(err) 157 } 158 if f := r.Free(); f != 1 { 159 t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, 1, f) 160 } 161 if f := r.Used(); f != (tc.free - 1) { 162 t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, tc.free-1, f) 163 } 164 if err := r.Allocate(released); err != nil { 165 t.Fatal(err) 166 } 167 if f := r.Free(); f != 0 { 168 t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, 0, f) 169 } 170 if f := r.Used(); f != tc.free { 171 t.Errorf("[%s] wrong free: expected %d, got %d", tc.name, tc.free, f) 172 } 173 }) 174 } 175 } 176 177 func TestAllocateTiny(t *testing.T) { 178 _, cidr, err := netutils.ParseCIDRSloppy("192.168.1.0/32") 179 if err != nil { 180 t.Fatal(err) 181 } 182 r, err := NewInMemory(cidr) 183 if err != nil { 184 t.Fatal(err) 185 } 186 if f := r.Free(); f != 0 { 187 t.Errorf("free: %d", f) 188 } 189 if _, err := r.AllocateNext(); err != ErrFull { 190 t.Error(err) 191 } 192 } 193 194 func TestAllocateReserved(t *testing.T) { 195 _, cidr, err := netutils.ParseCIDRSloppy("192.168.1.0/25") 196 if err != nil { 197 t.Fatal(err) 198 } 199 r, err := NewInMemory(cidr) 200 if err != nil { 201 t.Fatal(err) 202 } 203 // allocate all addresses on the dynamic block 204 // subnet /25 = 128 ; dynamic block size is min(max(16,128/16),256) = 16 205 dynamicOffset := calculateRangeOffset(cidr) 206 dynamicBlockSize := r.max - dynamicOffset 207 for i := 0; i < dynamicBlockSize; i++ { 208 if _, err := r.AllocateNext(); err != nil { 209 t.Errorf("Unexpected error trying to allocate: %v", err) 210 } 211 } 212 for i := dynamicOffset; i < r.max; i++ { 213 ip := fmt.Sprintf("192.168.1.%d", i+1) 214 if !r.Has(netutils.ParseIPSloppy(ip)) { 215 t.Errorf("IP %s expected to be allocated", ip) 216 } 217 } 218 if f := r.Free(); f != dynamicOffset { 219 t.Errorf("expected %d free addresses, got %d", dynamicOffset, f) 220 } 221 // allocate all addresses on the static block 222 for i := 0; i < dynamicOffset; i++ { 223 ip := fmt.Sprintf("192.168.1.%d", i+1) 224 if err := r.Allocate(netutils.ParseIPSloppy(ip)); err != nil { 225 t.Errorf("Unexpected error trying to allocate IP %s: %v", ip, err) 226 } 227 } 228 if f := r.Free(); f != 0 { 229 t.Errorf("expected free equal to 0 got: %d", f) 230 } 231 // release one address in the allocated block and another a new one randomly 232 if err := r.Release(netutils.ParseIPSloppy("192.168.1.10")); err != nil { 233 t.Fatalf("Unexpected error trying to release ip 192.168.1.10: %v", err) 234 } 235 if _, err := r.AllocateNext(); err != nil { 236 t.Error(err) 237 } 238 if f := r.Free(); f != 0 { 239 t.Errorf("expected free equal to 0 got: %d", f) 240 } 241 } 242 243 func TestAllocateSmall(t *testing.T) { 244 _, cidr, err := netutils.ParseCIDRSloppy("192.168.1.240/30") 245 if err != nil { 246 t.Fatal(err) 247 } 248 r, err := NewInMemory(cidr) 249 if err != nil { 250 t.Fatal(err) 251 } 252 if f := r.Free(); f != 2 { 253 t.Errorf("expected free equal to 2 got: %d", f) 254 } 255 found := sets.NewString() 256 for i := 0; i < 2; i++ { 257 ip, err := r.AllocateNext() 258 if err != nil { 259 t.Fatal(err) 260 } 261 if found.Has(ip.String()) { 262 t.Fatalf("address %s has been already allocated", ip) 263 } 264 found.Insert(ip.String()) 265 } 266 for s := range found { 267 if !r.Has(netutils.ParseIPSloppy(s)) { 268 t.Fatalf("missing: %s", s) 269 } 270 if err := r.Allocate(netutils.ParseIPSloppy(s)); err != ErrAllocated { 271 t.Fatal(err) 272 } 273 } 274 for i := 0; i < 100; i++ { 275 if _, err := r.AllocateNext(); err != ErrFull { 276 t.Fatalf("suddenly became not-full: %#v", r) 277 } 278 } 279 280 if f := r.Free(); f != 0 { 281 t.Errorf("expected free equal to 0 got: %d", f) 282 } 283 284 if r.max != 2 { 285 t.Fatalf("expected range equal to 2, got: %v", r) 286 } 287 } 288 289 func TestForEach(t *testing.T) { 290 _, cidr, err := netutils.ParseCIDRSloppy("192.168.1.0/24") 291 if err != nil { 292 t.Fatal(err) 293 } 294 295 testCases := []sets.String{ 296 sets.NewString(), 297 sets.NewString("192.168.1.1"), 298 sets.NewString("192.168.1.1", "192.168.1.254"), 299 sets.NewString("192.168.1.1", "192.168.1.128", "192.168.1.254"), 300 } 301 302 for i, tc := range testCases { 303 r, err := NewInMemory(cidr) 304 if err != nil { 305 t.Fatal(err) 306 } 307 for ips := range tc { 308 ip := netutils.ParseIPSloppy(ips) 309 if err := r.Allocate(ip); err != nil { 310 t.Errorf("[%d] error allocating IP %v: %v", i, ip, err) 311 } 312 if !r.Has(ip) { 313 t.Errorf("[%d] expected IP %v allocated", i, ip) 314 } 315 } 316 calls := sets.NewString() 317 r.ForEach(func(ip net.IP) { 318 calls.Insert(ip.String()) 319 }) 320 if len(calls) != len(tc) { 321 t.Errorf("[%d] expected %d calls, got %d", i, len(tc), len(calls)) 322 } 323 if !calls.Equal(tc) { 324 t.Errorf("[%d] expected calls to equal testcase: %v vs %v", i, calls.List(), tc.List()) 325 } 326 } 327 } 328 329 func TestSnapshot(t *testing.T) { 330 _, cidr, err := netutils.ParseCIDRSloppy("192.168.1.0/24") 331 if err != nil { 332 t.Fatal(err) 333 } 334 r, err := NewInMemory(cidr) 335 if err != nil { 336 t.Fatal(err) 337 } 338 ip := []net.IP{} 339 for i := 0; i < 10; i++ { 340 n, err := r.AllocateNext() 341 if err != nil { 342 t.Fatal(err) 343 } 344 ip = append(ip, n) 345 } 346 347 var dst api.RangeAllocation 348 err = r.Snapshot(&dst) 349 if err != nil { 350 t.Fatal(err) 351 } 352 353 _, network, err := netutils.ParseCIDRSloppy(dst.Range) 354 if err != nil { 355 t.Fatal(err) 356 } 357 358 if !network.IP.Equal(cidr.IP) || network.Mask.String() != cidr.Mask.String() { 359 t.Fatalf("mismatched networks: %s : %s", network, cidr) 360 } 361 362 _, otherCidr, err := netutils.ParseCIDRSloppy("192.168.2.0/24") 363 if err != nil { 364 t.Fatal(err) 365 } 366 _, err = NewInMemory(otherCidr) 367 if err != nil { 368 t.Fatal(err) 369 } 370 if err := r.Restore(otherCidr, dst.Data); err != ErrMismatchedNetwork { 371 t.Fatal(err) 372 } 373 other, err := NewInMemory(network) 374 if err != nil { 375 t.Fatal(err) 376 } 377 if err := other.Restore(network, dst.Data); err != nil { 378 t.Fatal(err) 379 } 380 381 for _, n := range ip { 382 if !other.Has(n) { 383 t.Errorf("restored range does not have %s", n) 384 } 385 } 386 if other.Free() != r.Free() { 387 t.Errorf("counts do not match: %d", other.Free()) 388 } 389 } 390 391 func TestNewFromSnapshot(t *testing.T) { 392 _, cidr, err := netutils.ParseCIDRSloppy("192.168.0.0/24") 393 if err != nil { 394 t.Fatal(err) 395 } 396 r, err := NewInMemory(cidr) 397 if err != nil { 398 t.Fatal(err) 399 } 400 allocated := []net.IP{} 401 for i := 0; i < 128; i++ { 402 ip, err := r.AllocateNext() 403 if err != nil { 404 t.Fatal(err) 405 } 406 allocated = append(allocated, ip) 407 } 408 409 snapshot := api.RangeAllocation{} 410 if err = r.Snapshot(&snapshot); err != nil { 411 t.Fatal(err) 412 } 413 414 r, err = NewFromSnapshot(&snapshot) 415 if err != nil { 416 t.Fatal(err) 417 } 418 419 if x := r.Free(); x != 126 { 420 t.Fatalf("expected 126 free IPs, got %d", x) 421 } 422 if x := r.Used(); x != 128 { 423 t.Fatalf("expected 128 used IPs, got %d", x) 424 } 425 426 for _, ip := range allocated { 427 if !r.Has(ip) { 428 t.Fatalf("expected IP to be allocated, but it was not") 429 } 430 } 431 } 432 433 func TestClusterIPMetrics(t *testing.T) { 434 clearMetrics() 435 // create IPv4 allocator 436 cidrIPv4 := "10.0.0.0/24" 437 _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy(cidrIPv4) 438 a, err := NewInMemory(clusterCIDRv4) 439 if err != nil { 440 t.Fatalf("unexpected error creating CidrSet: %v", err) 441 } 442 a.EnableMetrics() 443 // create IPv6 allocator 444 cidrIPv6 := "2001:db8::/112" 445 _, clusterCIDRv6, _ := netutils.ParseCIDRSloppy(cidrIPv6) 446 b, err := NewInMemory(clusterCIDRv6) 447 b.EnableMetrics() 448 if err != nil { 449 t.Fatalf("unexpected error creating CidrSet: %v", err) 450 } 451 452 // Check initial state 453 em := testMetrics{ 454 free: 0, 455 used: 0, 456 allocated: 0, 457 errors: 0, 458 } 459 expectMetrics(t, cidrIPv4, em) 460 em = testMetrics{ 461 free: 0, 462 used: 0, 463 allocated: 0, 464 errors: 0, 465 } 466 expectMetrics(t, cidrIPv6, em) 467 468 // allocate 2 IPv4 addresses 469 found := sets.NewString() 470 for i := 0; i < 2; i++ { 471 ip, err := a.AllocateNext() 472 if err != nil { 473 t.Fatal(err) 474 } 475 if found.Has(ip.String()) { 476 t.Fatalf("already reserved: %s", ip) 477 } 478 found.Insert(ip.String()) 479 } 480 481 em = testMetrics{ 482 free: 252, 483 used: 2, 484 allocated: 2, 485 errors: 0, 486 } 487 expectMetrics(t, cidrIPv4, em) 488 489 // try to allocate the same IP addresses 490 for s := range found { 491 if !a.Has(netutils.ParseIPSloppy(s)) { 492 t.Fatalf("missing: %s", s) 493 } 494 if err := a.Allocate(netutils.ParseIPSloppy(s)); err != ErrAllocated { 495 t.Fatal(err) 496 } 497 } 498 em = testMetrics{ 499 free: 252, 500 used: 2, 501 allocated: 2, 502 errors: 2, 503 } 504 expectMetrics(t, cidrIPv4, em) 505 506 // release the addresses allocated 507 for s := range found { 508 if !a.Has(netutils.ParseIPSloppy(s)) { 509 t.Fatalf("missing: %s", s) 510 } 511 if err := a.Release(netutils.ParseIPSloppy(s)); err != nil { 512 t.Fatal(err) 513 } 514 } 515 em = testMetrics{ 516 free: 254, 517 used: 0, 518 allocated: 2, 519 errors: 2, 520 } 521 expectMetrics(t, cidrIPv4, em) 522 523 // allocate 264 addresses for each allocator 524 // the full range and 10 more (254 + 10 = 264) for IPv4 525 for i := 0; i < 264; i++ { 526 a.AllocateNext() 527 b.AllocateNext() 528 } 529 em = testMetrics{ 530 free: 0, 531 used: 254, 532 allocated: 256, // this is a counter, we already had 2 allocations and we did 254 more 533 errors: 12, 534 } 535 expectMetrics(t, cidrIPv4, em) 536 em = testMetrics{ 537 free: 65271, // IPv6 clusterIP range is capped to 2^16 and consider the broadcast address as valid 538 used: 264, 539 allocated: 264, 540 errors: 0, 541 } 542 expectMetrics(t, cidrIPv6, em) 543 } 544 545 func TestClusterIPAllocatedMetrics(t *testing.T) { 546 clearMetrics() 547 // create IPv4 allocator 548 cidrIPv4 := "10.0.0.0/25" 549 _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy(cidrIPv4) 550 a, err := NewInMemory(clusterCIDRv4) 551 if err != nil { 552 t.Fatalf("unexpected error creating CidrSet: %v", err) 553 } 554 a.EnableMetrics() 555 556 em := testMetrics{ 557 free: 0, 558 used: 0, 559 allocated: 0, 560 errors: 0, 561 } 562 expectMetrics(t, cidrIPv4, em) 563 564 // allocate 2 dynamic IPv4 addresses 565 found := sets.NewString() 566 for i := 0; i < 2; i++ { 567 ip, err := a.AllocateNext() 568 if err != nil { 569 t.Fatal(err) 570 } 571 if found.Has(ip.String()) { 572 t.Fatalf("already reserved: %s", ip) 573 } 574 found.Insert(ip.String()) 575 } 576 577 dynamic_allocated, err := testutil.GetCounterMetricValue(clusterIPAllocations.WithLabelValues(cidrIPv4, "dynamic")) 578 if err != nil { 579 t.Errorf("failed to get %s value, err: %v", clusterIPAllocations.Name, err) 580 } 581 if dynamic_allocated != 2 { 582 t.Fatalf("Expected 2 received %f", dynamic_allocated) 583 } 584 585 // try to allocate the same IP addresses 586 for s := range found { 587 if !a.Has(netutils.ParseIPSloppy(s)) { 588 t.Fatalf("missing: %s", s) 589 } 590 if err := a.Allocate(netutils.ParseIPSloppy(s)); err != ErrAllocated { 591 t.Fatal(err) 592 } 593 } 594 595 static_errors, err := testutil.GetCounterMetricValue(clusterIPAllocationErrors.WithLabelValues(cidrIPv4, "static")) 596 if err != nil { 597 t.Errorf("failed to get %s value, err: %v", clusterIPAllocationErrors.Name, err) 598 } 599 if static_errors != 2 { 600 t.Fatalf("Expected 2 received %f", dynamic_allocated) 601 } 602 } 603 604 func TestMetricsDisabled(t *testing.T) { 605 // create metrics enabled allocator 606 cidrIPv4 := "10.0.0.0/24" 607 _, clusterCIDRv4, _ := netutils.ParseCIDRSloppy(cidrIPv4) 608 a, err := NewInMemory(clusterCIDRv4) 609 if err != nil { 610 t.Fatalf("unexpected error creating CidrSet: %v", err) 611 } 612 a.EnableMetrics() 613 614 // create metrics disabled allocator with same CIDR 615 // this metrics should be ignored 616 b, err := NewInMemory(clusterCIDRv4) 617 if err != nil { 618 t.Fatalf("unexpected error creating CidrSet: %v", err) 619 } 620 621 // Check initial state 622 em := testMetrics{ 623 free: 0, 624 used: 0, 625 allocated: 0, 626 errors: 0, 627 } 628 expectMetrics(t, cidrIPv4, em) 629 630 // allocate in metrics enabled allocator 631 for i := 0; i < 100; i++ { 632 _, err := a.AllocateNext() 633 if err != nil { 634 t.Fatal(err) 635 } 636 } 637 em = testMetrics{ 638 free: 154, 639 used: 100, 640 allocated: 100, 641 errors: 0, 642 } 643 expectMetrics(t, cidrIPv4, em) 644 645 // allocate in metrics disabled allocator 646 for i := 0; i < 200; i++ { 647 _, err := b.AllocateNext() 648 if err != nil { 649 t.Fatal(err) 650 } 651 } 652 // the metrics should not be changed 653 expectMetrics(t, cidrIPv4, em) 654 } 655 656 // Metrics helpers 657 func clearMetrics() { 658 clusterIPAllocated.Reset() 659 clusterIPAvailable.Reset() 660 clusterIPAllocations.Reset() 661 clusterIPAllocationErrors.Reset() 662 } 663 664 type testMetrics struct { 665 free float64 666 used float64 667 allocated float64 668 errors float64 669 } 670 671 func expectMetrics(t *testing.T, label string, em testMetrics) { 672 var m testMetrics 673 var err error 674 m.free, err = testutil.GetGaugeMetricValue(clusterIPAvailable.WithLabelValues(label)) 675 if err != nil { 676 t.Errorf("failed to get %s value, err: %v", clusterIPAvailable.Name, err) 677 } 678 m.used, err = testutil.GetGaugeMetricValue(clusterIPAllocated.WithLabelValues(label)) 679 if err != nil { 680 t.Errorf("failed to get %s value, err: %v", clusterIPAllocated.Name, err) 681 } 682 static_allocated, err := testutil.GetCounterMetricValue(clusterIPAllocations.WithLabelValues(label, "static")) 683 if err != nil { 684 t.Errorf("failed to get %s value, err: %v", clusterIPAllocations.Name, err) 685 } 686 static_errors, err := testutil.GetCounterMetricValue(clusterIPAllocationErrors.WithLabelValues(label, "static")) 687 if err != nil { 688 t.Errorf("failed to get %s value, err: %v", clusterIPAllocationErrors.Name, err) 689 } 690 dynamic_allocated, err := testutil.GetCounterMetricValue(clusterIPAllocations.WithLabelValues(label, "dynamic")) 691 if err != nil { 692 t.Errorf("failed to get %s value, err: %v", clusterIPAllocations.Name, err) 693 } 694 dynamic_errors, err := testutil.GetCounterMetricValue(clusterIPAllocationErrors.WithLabelValues(label, "dynamic")) 695 if err != nil { 696 t.Errorf("failed to get %s value, err: %v", clusterIPAllocationErrors.Name, err) 697 } 698 699 m.allocated = static_allocated + dynamic_allocated 700 m.errors = static_errors + dynamic_errors 701 702 if m != em { 703 t.Fatalf("metrics error: expected %v, received %v", em, m) 704 } 705 } 706 707 func TestDryRun(t *testing.T) { 708 testCases := []struct { 709 name string 710 cidr string 711 family api.IPFamily 712 }{{ 713 name: "IPv4", 714 cidr: "192.168.1.0/24", 715 family: api.IPv4Protocol, 716 }, { 717 name: "IPv6", 718 cidr: "2001:db8:1::/48", 719 family: api.IPv6Protocol, 720 }} 721 for _, tc := range testCases { 722 t.Run(tc.name, func(t *testing.T) { 723 _, cidr, err := netutils.ParseCIDRSloppy(tc.cidr) 724 if err != nil { 725 t.Fatalf("unexpected failure: %v", err) 726 } 727 r, err := NewInMemory(cidr) 728 if err != nil { 729 t.Fatalf("unexpected failure: %v", err) 730 } 731 732 baseUsed := r.Used() 733 734 rCIDR := r.DryRun().CIDR() 735 if rCIDR.String() != tc.cidr { 736 t.Errorf("allocator returned a different cidr") 737 } 738 739 if r.DryRun().IPFamily() != tc.family { 740 t.Errorf("allocator returned wrong IP family") 741 } 742 743 expectUsed := func(t *testing.T, r *Range, expect int) { 744 t.Helper() 745 if u := r.Used(); u != expect { 746 t.Errorf("unexpected used count: got %d, wanted %d", u, expect) 747 } 748 } 749 expectUsed(t, r, baseUsed) 750 751 err = r.DryRun().Allocate(netutils.AddIPOffset(netutils.BigForIP(cidr.IP), 1)) 752 if err != nil { 753 t.Fatalf("unexpected failure: %v", err) 754 } 755 expectUsed(t, r, baseUsed) 756 757 _, err = r.DryRun().AllocateNext() 758 if err != nil { 759 t.Fatalf("unexpected failure: %v", err) 760 } 761 expectUsed(t, r, baseUsed) 762 763 if err := r.DryRun().Release(cidr.IP); err != nil { 764 t.Fatalf("unexpected failure: %v", err) 765 } 766 expectUsed(t, r, baseUsed) 767 }) 768 } 769 } 770 771 func Test_calculateRangeOffset(t *testing.T) { 772 // default $min = 16, $max = 256 and $step = 16. 773 tests := []struct { 774 name string 775 cidr string 776 want int 777 }{ 778 { 779 name: "full mask IPv4", 780 cidr: "192.168.1.1/32", 781 want: 0, 782 }, 783 { 784 name: "full mask IPv6", 785 cidr: "fd00::1/128", 786 want: 0, 787 }, 788 { 789 name: "very small mask IPv4", 790 cidr: "192.168.1.1/30", 791 want: 0, 792 }, 793 { 794 name: "very small mask IPv6", 795 cidr: "fd00::1/126", 796 want: 0, 797 }, 798 { 799 name: "small mask IPv4", 800 cidr: "192.168.1.1/28", 801 want: 0, 802 }, 803 { 804 name: "small mask IPv4", 805 cidr: "192.168.1.1/27", 806 want: 16, 807 }, 808 { 809 name: "small mask IPv6", 810 cidr: "fd00::1/124", 811 want: 0, 812 }, 813 { 814 name: "small mask IPv6", 815 cidr: "fd00::1/122", 816 want: 16, 817 }, 818 { 819 name: "medium mask IPv4", 820 cidr: "192.168.1.1/22", 821 want: 64, 822 }, 823 { 824 name: "medium mask IPv6", 825 cidr: "fd00::1/118", 826 want: 64, 827 }, 828 { 829 name: "large mask IPv4", 830 cidr: "192.168.1.1/8", 831 want: 256, 832 }, 833 { 834 name: "large mask IPv6", 835 cidr: "fd00::1/12", 836 want: 256, 837 }, 838 } 839 for _, tt := range tests { 840 t.Run(tt.name, func(t *testing.T) { 841 842 _, cidr, err := netutils.ParseCIDRSloppy(tt.cidr) 843 if err != nil { 844 t.Fatalf("Unexpected error parsing CIDR %s: %v", tt.cidr, err) 845 } 846 if got := calculateRangeOffset(cidr); got != tt.want { 847 t.Errorf("DynamicRangeOffset() = %v, want %v", got, tt.want) 848 } 849 }) 850 } 851 } 852 853 // cpu: Intel(R) Xeon(R) CPU E5-2678 v3 @ 2.50GHz 854 // BenchmarkAllocateNextIPv4 855 // BenchmarkAllocateNextIPv4-24 1175304 870.9 ns/op 1337 B/op 11 allocs/op 856 func BenchmarkAllocateNextIPv4Size1048574(b *testing.B) { 857 _, cidr, err := netutils.ParseCIDRSloppy("10.0.0.0/12") 858 if err != nil { 859 b.Fatal(err) 860 } 861 r, err := NewInMemory(cidr) 862 if err != nil { 863 b.Fatal(err) 864 } 865 for n := 0; n < b.N; n++ { 866 r.AllocateNext() 867 } 868 } 869 870 // This is capped to 65535 871 // cpu: Intel(R) Xeon(R) CPU E5-2678 v3 @ 2.50GHz 872 // BenchmarkAllocateNextIPv6 873 // BenchmarkAllocateNextIPv6-24 5779431 194.0 ns/op 18 B/op 2 allocs/op 874 func BenchmarkAllocateNextIPv6Size65535(b *testing.B) { 875 _, cidr, err := netutils.ParseCIDRSloppy("fd00::/24") 876 if err != nil { 877 b.Fatal(err) 878 } 879 r, err := NewInMemory(cidr) 880 if err != nil { 881 b.Fatal(err) 882 } 883 for n := 0; n < b.N; n++ { 884 r.AllocateNext() 885 } 886 }