k8s.io/kubernetes@v1.29.3/pkg/controller/nodeipam/ipam/multicidrset/multi_cidr_set_test.go (about) 1 /* 2 Copyright 2022 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 multicidrset 18 19 import ( 20 "net" 21 "reflect" 22 "testing" 23 24 "k8s.io/component-base/metrics/testutil" 25 "k8s.io/klog/v2/ktesting" 26 utilnet "k8s.io/utils/net" 27 ) 28 29 func allocateNext(s *MultiCIDRSet) (*net.IPNet, error) { 30 candidate, _, err := s.NextCandidate() 31 if err != nil { 32 return nil, err 33 } 34 35 err = s.Occupy(candidate) 36 37 return candidate, err 38 } 39 40 func TestCIDRSetFullyAllocated(t *testing.T) { 41 cases := []struct { 42 clusterCIDRStr string 43 perNodeHostBits int 44 expectedCIDR string 45 description string 46 }{ 47 { 48 clusterCIDRStr: "127.123.234.0/28", 49 perNodeHostBits: 4, 50 expectedCIDR: "127.123.234.0/28", 51 description: "Fully allocated CIDR with IPv4", 52 }, 53 { 54 clusterCIDRStr: "beef:1234::/112", 55 perNodeHostBits: 16, 56 expectedCIDR: "beef:1234::/112", 57 description: "Fully allocated CIDR with IPv6", 58 }, 59 } 60 for _, tc := range cases { 61 _, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr) 62 a, err := NewMultiCIDRSet(clusterCIDR, tc.perNodeHostBits) 63 if err != nil { 64 t.Fatalf("unexpected error: %v for %v", err, tc.description) 65 } 66 p, err := allocateNext(a) 67 if err != nil { 68 t.Fatalf("unexpected error: %v for %v", err, tc.description) 69 } 70 if p.String() != tc.expectedCIDR { 71 t.Fatalf("unexpected allocated cidr: %v, expecting %v for %v", 72 p.String(), tc.expectedCIDR, tc.description) 73 } 74 75 _, err = allocateNext(a) 76 if err == nil { 77 t.Fatalf("expected error because of fully-allocated range for %v", tc.description) 78 } 79 80 a.Release(p) 81 82 p, err = allocateNext(a) 83 if err != nil { 84 t.Fatalf("unexpected error: %v for %v", err, tc.description) 85 } 86 if p.String() != tc.expectedCIDR { 87 t.Fatalf("unexpected allocated cidr: %v, expecting %v for %v", 88 p.String(), tc.expectedCIDR, tc.description) 89 } 90 _, err = allocateNext(a) 91 if err == nil { 92 t.Fatalf("expected error because of fully-allocated range for %v", tc.description) 93 } 94 } 95 } 96 97 func TestIndexToCIDRBlock(t *testing.T) { 98 cases := []struct { 99 clusterCIDRStr string 100 perNodeHostBits int 101 index int 102 CIDRBlock string 103 description string 104 }{ 105 { 106 clusterCIDRStr: "127.123.3.0/16", 107 perNodeHostBits: 8, 108 index: 0, 109 CIDRBlock: "127.123.0.0/24", 110 description: "1st IP address indexed with IPv4", 111 }, 112 { 113 clusterCIDRStr: "127.123.0.0/16", 114 perNodeHostBits: 8, 115 index: 15, 116 CIDRBlock: "127.123.15.0/24", 117 description: "16th IP address indexed with IPv4", 118 }, 119 { 120 clusterCIDRStr: "192.168.5.219/28", 121 perNodeHostBits: 0, 122 index: 5, 123 CIDRBlock: "192.168.5.213/32", 124 description: "5th IP address indexed with IPv4", 125 }, 126 { 127 clusterCIDRStr: "2001:0db8:1234:3::/48", 128 perNodeHostBits: 64, 129 index: 0, 130 CIDRBlock: "2001:db8:1234::/64", 131 description: "1st IP address indexed with IPv6 /64", 132 }, 133 { 134 clusterCIDRStr: "2001:0db8:1234::/48", 135 perNodeHostBits: 64, 136 index: 15, 137 CIDRBlock: "2001:db8:1234:f::/64", 138 description: "16th IP address indexed with IPv6 /64", 139 }, 140 { 141 clusterCIDRStr: "2001:0db8:85a3::8a2e:0370:7334/50", 142 perNodeHostBits: 65, 143 index: 6425, 144 CIDRBlock: "2001:db8:85a3:3232::/63", 145 description: "6426th IP address indexed with IPv6 /63", 146 }, 147 { 148 clusterCIDRStr: "2001:0db8::/32", 149 perNodeHostBits: 80, 150 index: 0, 151 CIDRBlock: "2001:db8::/48", 152 description: "1st IP address indexed with IPv6 /48", 153 }, 154 { 155 clusterCIDRStr: "2001:0db8::/32", 156 perNodeHostBits: 80, 157 index: 15, 158 CIDRBlock: "2001:db8:f::/48", 159 description: "16th IP address indexed with IPv6 /48", 160 }, 161 { 162 clusterCIDRStr: "2001:0db8:85a3::8a2e:0370:7334/32", 163 perNodeHostBits: 80, 164 index: 6425, 165 CIDRBlock: "2001:db8:1919::/48", 166 description: "6426th IP address indexed with IPv6 /48", 167 }, 168 { 169 clusterCIDRStr: "2001:0db8:1234:ff00::/56", 170 perNodeHostBits: 56, 171 index: 0, 172 CIDRBlock: "2001:db8:1234:ff00::/72", 173 description: "1st IP address indexed with IPv6 /72", 174 }, 175 { 176 clusterCIDRStr: "2001:0db8:1234:ff00::/56", 177 perNodeHostBits: 56, 178 index: 15, 179 CIDRBlock: "2001:db8:1234:ff00:f00::/72", 180 description: "16th IP address indexed with IPv6 /72", 181 }, 182 { 183 clusterCIDRStr: "2001:0db8:1234:ff00::0370:7334/56", 184 perNodeHostBits: 56, 185 index: 6425, 186 CIDRBlock: "2001:db8:1234:ff19:1900::/72", 187 description: "6426th IP address indexed with IPv6 /72", 188 }, 189 { 190 clusterCIDRStr: "2001:0db8:1234:0:1234::/80", 191 perNodeHostBits: 32, 192 index: 0, 193 CIDRBlock: "2001:db8:1234:0:1234::/96", 194 description: "1st IP address indexed with IPv6 /96", 195 }, 196 { 197 clusterCIDRStr: "2001:0db8:1234:0:1234::/80", 198 perNodeHostBits: 32, 199 index: 15, 200 CIDRBlock: "2001:db8:1234:0:1234:f::/96", 201 description: "16th IP address indexed with IPv6 /96", 202 }, 203 { 204 clusterCIDRStr: "2001:0db8:1234:ff00::0370:7334/80", 205 perNodeHostBits: 32, 206 index: 6425, 207 CIDRBlock: "2001:db8:1234:ff00:0:1919::/96", 208 description: "6426th IP address indexed with IPv6 /96", 209 }, 210 } 211 for _, tc := range cases { 212 _, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr) 213 a, err := NewMultiCIDRSet(clusterCIDR, tc.perNodeHostBits) 214 if err != nil { 215 t.Fatalf("error for %v ", tc.description) 216 } 217 cidr, err := a.indexToCIDRBlock(tc.index) 218 if err != nil { 219 t.Fatalf("error for %v ", tc.description) 220 } 221 if cidr.String() != tc.CIDRBlock { 222 t.Fatalf("error for %v index %d %s", tc.description, tc.index, cidr.String()) 223 } 224 } 225 } 226 227 func TestCIDRSet_RandomishAllocation(t *testing.T) { 228 cases := []struct { 229 clusterCIDRStr string 230 description string 231 }{ 232 { 233 clusterCIDRStr: "127.123.234.0/16", 234 description: "RandomishAllocation with IPv4", 235 }, 236 { 237 clusterCIDRStr: "beef:1234::/112", 238 description: "RandomishAllocation with IPv6", 239 }, 240 } 241 for _, tc := range cases { 242 _, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr) 243 a, err := NewMultiCIDRSet(clusterCIDR, 8) 244 if err != nil { 245 t.Fatalf("Error allocating CIDRSet for %v", tc.description) 246 } 247 // allocate all the CIDRs. 248 var cidrs []*net.IPNet 249 250 for i := 0; i < 256; i++ { 251 if c, err := allocateNext(a); err == nil { 252 cidrs = append(cidrs, c) 253 } else { 254 t.Fatalf("unexpected error: %v for %v", err, tc.description) 255 } 256 } 257 258 _, err = allocateNext(a) 259 if err == nil { 260 t.Fatalf("expected error because of fully-allocated range for %v", tc.description) 261 } 262 // release all the CIDRs. 263 for i := 0; i < len(cidrs); i++ { 264 a.Release(cidrs[i]) 265 } 266 267 // allocate the CIDRs again. 268 var rcidrs []*net.IPNet 269 for i := 0; i < 256; i++ { 270 if c, err := allocateNext(a); err == nil { 271 rcidrs = append(rcidrs, c) 272 } else { 273 t.Fatalf("unexpected error: %d, %v for %v", i, err, tc.description) 274 } 275 } 276 _, err = allocateNext(a) 277 if err == nil { 278 t.Fatalf("expected error because of fully-allocated range for %v", tc.description) 279 } 280 281 if !reflect.DeepEqual(cidrs, rcidrs) { 282 t.Fatalf("expected re-allocated cidrs are the same collection for %v", tc.description) 283 } 284 } 285 } 286 287 func TestCIDRSet_AllocationOccupied(t *testing.T) { 288 cases := []struct { 289 clusterCIDRStr string 290 description string 291 }{ 292 { 293 clusterCIDRStr: "127.123.234.0/16", 294 description: "AllocationOccupied with IPv4", 295 }, 296 { 297 clusterCIDRStr: "beef:1234::/112", 298 description: "AllocationOccupied with IPv6", 299 }, 300 } 301 for _, tc := range cases { 302 _, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr) 303 a, err := NewMultiCIDRSet(clusterCIDR, 8) 304 if err != nil { 305 t.Fatalf("Error allocating CIDRSet for %v", tc.description) 306 } 307 // allocate all the CIDRs. 308 var cidrs []*net.IPNet 309 var numCIDRs = 256 310 311 for i := 0; i < numCIDRs; i++ { 312 if c, err := allocateNext(a); err == nil { 313 cidrs = append(cidrs, c) 314 } else { 315 t.Fatalf("unexpected error: %v for %v", err, tc.description) 316 } 317 } 318 319 _, err = allocateNext(a) 320 if err == nil { 321 t.Fatalf("expected error because of fully-allocated range for %v", tc.description) 322 } 323 // release all the CIDRs. 324 for i := 0; i < len(cidrs); i++ { 325 a.Release(cidrs[i]) 326 } 327 // occupy the last 128 CIDRs. 328 for i := numCIDRs / 2; i < numCIDRs; i++ { 329 a.Occupy(cidrs[i]) 330 } 331 // occupy the first of the last 128 again. 332 a.Occupy(cidrs[numCIDRs/2]) 333 334 // allocate the first 128 CIDRs again. 335 var rcidrs []*net.IPNet 336 for i := 0; i < numCIDRs/2; i++ { 337 if c, err := allocateNext(a); err == nil { 338 rcidrs = append(rcidrs, c) 339 } else { 340 t.Fatalf("unexpected error: %d, %v for %v", i, err, tc.description) 341 } 342 } 343 _, err = allocateNext(a) 344 if err == nil { 345 t.Fatalf("expected error because of fully-allocated range for %v", tc.description) 346 } 347 348 // check Occupy() works properly. 349 for i := numCIDRs / 2; i < numCIDRs; i++ { 350 rcidrs = append(rcidrs, cidrs[i]) 351 } 352 if !reflect.DeepEqual(cidrs, rcidrs) { 353 t.Fatalf("expected re-allocated cidrs are the same collection for %v", tc.description) 354 } 355 } 356 } 357 358 func TestDoubleOccupyRelease(t *testing.T) { 359 // Run a sequence of operations and check the number of occupied CIDRs 360 // after each one. 361 clusterCIDRStr := "10.42.0.0/16" 362 operations := []struct { 363 cidrStr string 364 operation string 365 numOccupied int 366 }{ 367 // Occupy 1 element: +1 368 { 369 cidrStr: "10.42.5.0/24", 370 operation: "occupy", 371 numOccupied: 1, 372 }, 373 // Occupy 1 more element: +1 374 { 375 cidrStr: "10.42.9.0/24", 376 operation: "occupy", 377 numOccupied: 2, 378 }, 379 // Occupy 4 elements overlapping with one from the above: +3 380 { 381 cidrStr: "10.42.8.0/22", 382 operation: "occupy", 383 numOccupied: 5, 384 }, 385 // Occupy an already-occupied element: no change 386 { 387 cidrStr: "10.42.9.0/24", 388 operation: "occupy", 389 numOccupied: 5, 390 }, 391 // Release an coccupied element: -1 392 { 393 cidrStr: "10.42.9.0/24", 394 operation: "release", 395 numOccupied: 4, 396 }, 397 // Release an unoccupied element: no change 398 { 399 cidrStr: "10.42.9.0/24", 400 operation: "release", 401 numOccupied: 4, 402 }, 403 // Release 4 elements, only one of which is occupied: -1 404 { 405 cidrStr: "10.42.4.0/22", 406 operation: "release", 407 numOccupied: 3, 408 }, 409 } 410 // Check that there are exactly that many allocatable CIDRs after all 411 // operations have been executed. 412 numAllocatable24s := (1 << 8) - 3 413 414 _, clusterCIDR, _ := utilnet.ParseCIDRSloppy(clusterCIDRStr) 415 a, err := NewMultiCIDRSet(clusterCIDR, 8) 416 if err != nil { 417 t.Fatalf("Error allocating CIDRSet") 418 } 419 420 // Execute the operations. 421 for _, op := range operations { 422 _, cidr, _ := utilnet.ParseCIDRSloppy(op.cidrStr) 423 switch op.operation { 424 case "occupy": 425 a.Occupy(cidr) 426 case "release": 427 a.Release(cidr) 428 default: 429 t.Fatalf("test error: unknown operation %v", op.operation) 430 } 431 if a.allocatedCIDRs != op.numOccupied { 432 t.Fatalf("CIDR %v Expected %d occupied CIDRS, got %d", cidr, op.numOccupied, a.allocatedCIDRs) 433 } 434 } 435 436 // Make sure that we can allocate exactly `numAllocatable24s` elements. 437 for i := 0; i < numAllocatable24s; i++ { 438 _, err := allocateNext(a) 439 if err != nil { 440 t.Fatalf("Expected to be able to allocate %d CIDRS, failed after %d", numAllocatable24s, i) 441 } 442 } 443 444 _, err = allocateNext(a) 445 if err == nil { 446 t.Fatalf("Expected to be able to allocate exactly %d CIDRS, got one more", numAllocatable24s) 447 } 448 } 449 450 func TestGetBitforCIDR(t *testing.T) { 451 cases := []struct { 452 clusterCIDRStr string 453 perNodeHostBits int 454 subNetCIDRStr string 455 expectedBit int 456 expectErr bool 457 description string 458 }{ 459 { 460 clusterCIDRStr: "127.0.0.0/8", 461 perNodeHostBits: 16, 462 subNetCIDRStr: "127.0.0.0/16", 463 expectedBit: 0, 464 expectErr: false, 465 description: "Get 0 Bit with IPv4", 466 }, 467 { 468 clusterCIDRStr: "be00::/8", 469 perNodeHostBits: 112, 470 subNetCIDRStr: "be00::/16", 471 expectedBit: 0, 472 expectErr: false, 473 description: "Get 0 Bit with IPv6", 474 }, 475 { 476 clusterCIDRStr: "127.0.0.0/8", 477 perNodeHostBits: 16, 478 subNetCIDRStr: "127.123.0.0/16", 479 expectedBit: 123, 480 expectErr: false, 481 description: "Get 123rd Bit with IPv4", 482 }, 483 { 484 clusterCIDRStr: "be00::/8", 485 perNodeHostBits: 112, 486 subNetCIDRStr: "beef::/16", 487 expectedBit: 0xef, 488 expectErr: false, 489 description: "Get xef Bit with IPv6", 490 }, 491 { 492 clusterCIDRStr: "127.0.0.0/8", 493 perNodeHostBits: 16, 494 subNetCIDRStr: "127.168.0.0/16", 495 expectedBit: 168, 496 expectErr: false, 497 description: "Get 168th Bit with IPv4", 498 }, 499 { 500 clusterCIDRStr: "be00::/8", 501 perNodeHostBits: 112, 502 subNetCIDRStr: "be68::/16", 503 expectedBit: 0x68, 504 expectErr: false, 505 description: "Get x68th Bit with IPv6", 506 }, 507 { 508 clusterCIDRStr: "127.0.0.0/8", 509 perNodeHostBits: 16, 510 subNetCIDRStr: "127.224.0.0/16", 511 expectedBit: 224, 512 expectErr: false, 513 description: "Get 224th Bit with IPv4", 514 }, 515 { 516 clusterCIDRStr: "be00::/8", 517 perNodeHostBits: 112, 518 subNetCIDRStr: "be24::/16", 519 expectedBit: 0x24, 520 expectErr: false, 521 description: "Get x24th Bit with IPv6", 522 }, 523 { 524 clusterCIDRStr: "192.168.0.0/16", 525 perNodeHostBits: 8, 526 subNetCIDRStr: "192.168.12.0/24", 527 expectedBit: 12, 528 expectErr: false, 529 description: "Get 12th Bit with IPv4", 530 }, 531 { 532 clusterCIDRStr: "beef::/16", 533 perNodeHostBits: 104, 534 subNetCIDRStr: "beef:1200::/24", 535 expectedBit: 0x12, 536 expectErr: false, 537 description: "Get x12th Bit with IPv6", 538 }, 539 { 540 clusterCIDRStr: "192.168.0.0/16", 541 perNodeHostBits: 8, 542 subNetCIDRStr: "192.168.151.0/24", 543 expectedBit: 151, 544 expectErr: false, 545 description: "Get 151st Bit with IPv4", 546 }, 547 { 548 clusterCIDRStr: "beef::/16", 549 perNodeHostBits: 104, 550 subNetCIDRStr: "beef:9700::/24", 551 expectedBit: 0x97, 552 expectErr: false, 553 description: "Get x97st Bit with IPv6", 554 }, 555 { 556 clusterCIDRStr: "192.168.0.0/16", 557 perNodeHostBits: 8, 558 subNetCIDRStr: "127.168.224.0/24", 559 expectErr: true, 560 description: "Get error with IPv4", 561 }, 562 { 563 clusterCIDRStr: "beef::/16", 564 perNodeHostBits: 104, 565 subNetCIDRStr: "2001:db00::/24", 566 expectErr: true, 567 description: "Get error with IPv6", 568 }, 569 } 570 571 logger, _ := ktesting.NewTestContext(t) 572 for _, tc := range cases { 573 _, clusterCIDR, err := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr) 574 if err != nil { 575 t.Fatalf("unexpected error: %v for %v", err, tc.description) 576 } 577 578 cs, err := NewMultiCIDRSet(clusterCIDR, tc.perNodeHostBits) 579 if err != nil { 580 t.Fatalf("Error allocating CIDRSet for %v", tc.description) 581 } 582 _, subnetCIDR, err := utilnet.ParseCIDRSloppy(tc.subNetCIDRStr) 583 if err != nil { 584 t.Fatalf("unexpected error: %v for %v", err, tc.description) 585 } 586 587 got, err := cs.getIndexForIP(subnetCIDR.IP) 588 if err == nil && tc.expectErr { 589 logger.Error(nil, "Expected error but got null", "description", tc.description) 590 continue 591 } 592 593 if err != nil && !tc.expectErr { 594 logger.Error(err, "Unexpected error", "description", tc.description) 595 continue 596 } 597 598 if got != tc.expectedBit { 599 logger.Error(nil, "Unexpected value", "description", tc.description, "expected", tc.expectedBit, "got", got) 600 } 601 } 602 } 603 604 func TestCIDRSetv6(t *testing.T) { 605 cases := []struct { 606 clusterCIDRStr string 607 perNodeHostBits int 608 expectedCIDR string 609 expectedCIDR2 string 610 expectErr bool 611 description string 612 }{ 613 { 614 clusterCIDRStr: "127.0.0.0/8", 615 perNodeHostBits: 0, 616 expectErr: false, 617 expectedCIDR: "127.0.0.0/32", 618 expectedCIDR2: "127.0.0.1/32", 619 description: "Max cluster subnet size with IPv4", 620 }, 621 { 622 clusterCIDRStr: "beef:1234::/32", 623 perNodeHostBits: 79, 624 expectErr: true, 625 description: "Max cluster subnet size with IPv6", 626 }, 627 { 628 clusterCIDRStr: "2001:beef:1234:369b::/60", 629 perNodeHostBits: 64, 630 expectedCIDR: "2001:beef:1234:3690::/64", 631 expectedCIDR2: "2001:beef:1234:3691::/64", 632 expectErr: false, 633 description: "Allocate a few IPv6", 634 }, 635 } 636 for _, tc := range cases { 637 t.Run(tc.description, func(t *testing.T) { 638 _, clusterCIDR, _ := utilnet.ParseCIDRSloppy(tc.clusterCIDRStr) 639 a, err := NewMultiCIDRSet(clusterCIDR, tc.perNodeHostBits) 640 if gotErr := err != nil; gotErr != tc.expectErr { 641 t.Fatalf("NewMultiCIDRSet(%v, %v) = %v, %v; gotErr = %t, want %t", clusterCIDR, tc.perNodeHostBits, a, err, gotErr, tc.expectErr) 642 } 643 if a == nil { 644 return 645 } 646 p, err := allocateNext(a) 647 if err == nil && tc.expectErr { 648 t.Errorf("allocateNext(a) = nil, want error") 649 } 650 if err != nil && !tc.expectErr { 651 t.Errorf("allocateNext(a) = %+v, want no error", err) 652 } 653 if !tc.expectErr { 654 if p != nil && p.String() != tc.expectedCIDR { 655 t.Fatalf("allocateNext(a) got %+v, want %+v", p.String(), tc.expectedCIDR) 656 } 657 } 658 p2, err := allocateNext(a) 659 if err == nil && tc.expectErr { 660 t.Errorf("allocateNext(a) = nil, want error") 661 } 662 if err != nil && !tc.expectErr { 663 t.Errorf("allocateNext(a) = %+v, want no error", err) 664 } 665 if !tc.expectErr { 666 if p2 != nil && p2.String() != tc.expectedCIDR2 { 667 t.Fatalf("allocateNext(a) got %+v, want %+v", p2.String(), tc.expectedCIDR) 668 } 669 } 670 }) 671 } 672 } 673 674 func TestMultiCIDRSetMetrics(t *testing.T) { 675 cidr := "10.0.0.0/16" 676 _, clusterCIDR, _ := utilnet.ParseCIDRSloppy(cidr) 677 clearMetrics(map[string]string{"clusterCIDR": cidr}) 678 679 // We have 256 free cidrs 680 a, err := NewMultiCIDRSet(clusterCIDR, 8) 681 if err != nil { 682 t.Fatalf("unexpected error creating MultiCIDRSet: %v", err) 683 } 684 685 clusterMaskSize, _ := clusterCIDR.Mask.Size() 686 max := getMaxCIDRs(24, clusterMaskSize) 687 em := testMetrics{ 688 usage: 0, 689 allocs: 0, 690 releases: 0, 691 allocTries: 0, 692 max: float64(max), 693 } 694 expectMetrics(t, cidr, em) 695 696 // Allocate next all. 697 for i := 1; i <= 256; i++ { 698 _, err := allocateNext(a) 699 if err != nil { 700 t.Fatalf("unexpected error allocating a new CIDR: %v", err) 701 } 702 em := testMetrics{ 703 usage: float64(i) / float64(256), 704 allocs: float64(i), 705 releases: 0, 706 allocTries: 0, 707 max: float64(max), 708 } 709 expectMetrics(t, cidr, em) 710 } 711 // Release all CIDRs. 712 a.Release(clusterCIDR) 713 em = testMetrics{ 714 usage: 0, 715 allocs: 256, 716 releases: 256, 717 allocTries: 0, 718 max: float64(max), 719 } 720 expectMetrics(t, cidr, em) 721 722 // Allocate all CIDRs. 723 a.Occupy(clusterCIDR) 724 em = testMetrics{ 725 usage: 1, 726 allocs: 512, 727 releases: 256, 728 allocTries: 0, 729 max: float64(max), 730 } 731 expectMetrics(t, cidr, em) 732 733 } 734 735 func TestMultiCIDRSetMetricsHistogram(t *testing.T) { 736 cidr := "10.0.0.0/16" 737 _, clusterCIDR, _ := utilnet.ParseCIDRSloppy(cidr) 738 clearMetrics(map[string]string{"clusterCIDR": cidr}) 739 740 // We have 256 free cidrs. 741 a, err := NewMultiCIDRSet(clusterCIDR, 8) 742 if err != nil { 743 t.Fatalf("unexpected error creating MultiCIDRSet: %v", err) 744 } 745 746 clusterMaskSize, _ := clusterCIDR.Mask.Size() 747 max := getMaxCIDRs(24, clusterMaskSize) 748 em := testMetrics{ 749 usage: 0, 750 allocs: 0, 751 releases: 0, 752 allocTries: 0, 753 max: float64(max), 754 } 755 expectMetrics(t, cidr, em) 756 757 // Allocate half of the range. 758 // Occupy does not update the nextCandidate. 759 _, halfClusterCIDR, _ := utilnet.ParseCIDRSloppy("10.0.0.0/17") 760 a.Occupy(halfClusterCIDR) 761 em = testMetrics{ 762 usage: 0.5, 763 allocs: 128, 764 releases: 0, 765 max: float64(max), 766 } 767 expectMetrics(t, cidr, em) 768 // Allocate next should iterate until the next free cidr 769 // that is exactly the same number we allocated previously. 770 _, err = allocateNext(a) 771 if err != nil { 772 t.Fatalf("unexpected error allocating a new CIDR: %v", err) 773 } 774 em = testMetrics{ 775 usage: float64(129) / float64(256), 776 allocs: 129, 777 releases: 0, 778 max: float64(max), 779 } 780 expectMetrics(t, cidr, em) 781 } 782 783 func TestMultiCIDRSetMetricsDual(t *testing.T) { 784 // create IPv4 cidrSet. 785 cidrIPv4 := "10.0.0.0/16" 786 _, clusterCIDRv4, _ := utilnet.ParseCIDRSloppy(cidrIPv4) 787 clearMetrics(map[string]string{"clusterCIDR": cidrIPv4}) 788 789 a, err := NewMultiCIDRSet(clusterCIDRv4, 8) 790 if err != nil { 791 t.Fatalf("unexpected error creating MultiCIDRSet: %v", err) 792 } 793 794 clusterMaskSize, _ := clusterCIDRv4.Mask.Size() 795 maxIPv4 := getMaxCIDRs(24, clusterMaskSize) 796 em := testMetrics{ 797 usage: 0, 798 allocs: 0, 799 releases: 0, 800 allocTries: 0, 801 max: float64(maxIPv4), 802 } 803 expectMetrics(t, cidrIPv4, em) 804 805 // create IPv6 cidrSet. 806 cidrIPv6 := "2001:db8::/48" 807 _, clusterCIDRv6, _ := utilnet.ParseCIDRSloppy(cidrIPv6) 808 clearMetrics(map[string]string{"clusterCIDR": cidrIPv6}) 809 810 b, err := NewMultiCIDRSet(clusterCIDRv6, 64) 811 if err != nil { 812 t.Fatalf("unexpected error creating MultiCIDRSet: %v", err) 813 } 814 815 clusterMaskSize, _ = clusterCIDRv6.Mask.Size() 816 maxIPv6 := getMaxCIDRs(64, clusterMaskSize) 817 em = testMetrics{ 818 usage: 0, 819 allocs: 0, 820 releases: 0, 821 allocTries: 0, 822 max: float64(maxIPv6), 823 } 824 expectMetrics(t, cidrIPv6, em) 825 826 // Allocate all. 827 a.Occupy(clusterCIDRv4) 828 em = testMetrics{ 829 usage: 1, 830 allocs: 256, 831 releases: 0, 832 allocTries: 0, 833 max: float64(maxIPv4), 834 } 835 expectMetrics(t, cidrIPv4, em) 836 837 b.Occupy(clusterCIDRv6) 838 em = testMetrics{ 839 usage: 1, 840 allocs: 65536, 841 releases: 0, 842 allocTries: 0, 843 max: float64(maxIPv6), 844 } 845 expectMetrics(t, cidrIPv6, em) 846 847 // Release all. 848 a.Release(clusterCIDRv4) 849 em = testMetrics{ 850 usage: 0, 851 allocs: 256, 852 releases: 256, 853 allocTries: 0, 854 max: float64(maxIPv4), 855 } 856 expectMetrics(t, cidrIPv4, em) 857 b.Release(clusterCIDRv6) 858 em = testMetrics{ 859 usage: 0, 860 allocs: 65536, 861 releases: 65536, 862 allocTries: 0, 863 max: float64(maxIPv6), 864 } 865 expectMetrics(t, cidrIPv6, em) 866 867 } 868 869 func Test_getMaxCIDRs(t *testing.T) { 870 cidrIPv4 := "10.0.0.0/16" 871 _, clusterCIDRv4, _ := utilnet.ParseCIDRSloppy(cidrIPv4) 872 873 cidrIPv6 := "2001:db8::/48" 874 _, clusterCIDRv6, _ := utilnet.ParseCIDRSloppy(cidrIPv6) 875 876 tests := []struct { 877 name string 878 subNetMaskSize int 879 clusterCIDR *net.IPNet 880 expectedMaxCIDRs int 881 }{ 882 { 883 name: "IPv4", 884 subNetMaskSize: 24, 885 clusterCIDR: clusterCIDRv4, 886 expectedMaxCIDRs: 256, 887 }, 888 { 889 name: "IPv6", 890 subNetMaskSize: 64, 891 clusterCIDR: clusterCIDRv6, 892 expectedMaxCIDRs: 65536, 893 }, 894 } 895 896 for _, test := range tests { 897 t.Run(test.name, func(t *testing.T) { 898 clusterMaskSize, _ := test.clusterCIDR.Mask.Size() 899 maxCIDRs := getMaxCIDRs(test.subNetMaskSize, clusterMaskSize) 900 if test.expectedMaxCIDRs != maxCIDRs { 901 t.Errorf("incorrect maxCIDRs, expected: %d, got: %d", test.expectedMaxCIDRs, maxCIDRs) 902 } 903 }) 904 } 905 } 906 907 // Metrics helpers. 908 func clearMetrics(labels map[string]string) { 909 cidrSetAllocations.Delete(labels) 910 cidrSetReleases.Delete(labels) 911 cidrSetUsage.Delete(labels) 912 cidrSetAllocationTriesPerRequest.Delete(labels) 913 cidrSetMaxCidrs.Delete(labels) 914 } 915 916 type testMetrics struct { 917 usage float64 918 allocs float64 919 releases float64 920 allocTries float64 921 max float64 922 } 923 924 func expectMetrics(t *testing.T, label string, em testMetrics) { 925 var m testMetrics 926 var err error 927 m.usage, err = testutil.GetGaugeMetricValue(cidrSetUsage.WithLabelValues(label)) 928 if err != nil { 929 t.Errorf("failed to get %s value, err: %v", cidrSetUsage.Name, err) 930 } 931 m.allocs, err = testutil.GetCounterMetricValue(cidrSetAllocations.WithLabelValues(label)) 932 if err != nil { 933 t.Errorf("failed to get %s value, err: %v", cidrSetAllocations.Name, err) 934 } 935 m.releases, err = testutil.GetCounterMetricValue(cidrSetReleases.WithLabelValues(label)) 936 if err != nil { 937 t.Errorf("failed to get %s value, err: %v", cidrSetReleases.Name, err) 938 } 939 m.allocTries, err = testutil.GetHistogramMetricValue(cidrSetAllocationTriesPerRequest.WithLabelValues(label)) 940 if err != nil { 941 t.Errorf("failed to get %s value, err: %v", cidrSetAllocationTriesPerRequest.Name, err) 942 } 943 m.max, err = testutil.GetGaugeMetricValue(cidrSetMaxCidrs.WithLabelValues(label)) 944 if err != nil { 945 t.Errorf("failed to get %s value, err: %v", cidrSetMaxCidrs.Name, err) 946 } 947 948 if m != em { 949 t.Fatalf("metrics error: expected %v, received %v", em, m) 950 } 951 } 952 953 // Benchmarks 954 func benchmarkAllocateAllIPv6(cidr string, perNodeHostBits int, b *testing.B) { 955 _, clusterCIDR, _ := utilnet.ParseCIDRSloppy(cidr) 956 a, _ := NewMultiCIDRSet(clusterCIDR, perNodeHostBits) 957 for n := 0; n < b.N; n++ { 958 // Allocate the whole range + 1. 959 for i := 0; i <= a.MaxCIDRs; i++ { 960 allocateNext(a) 961 } 962 // Release all. 963 a.Release(clusterCIDR) 964 } 965 } 966 967 func BenchmarkAllocateAll_48_52(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/48", 52, b) } 968 func BenchmarkAllocateAll_48_56(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/48", 56, b) } 969 970 func BenchmarkAllocateAll_48_60(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/48", 60, b) } 971 func BenchmarkAllocateAll_48_64(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/48", 64, b) } 972 973 func BenchmarkAllocateAll_64_68(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/64", 68, b) } 974 975 func BenchmarkAllocateAll_64_72(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/64", 72, b) } 976 func BenchmarkAllocateAll_64_76(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/64", 76, b) } 977 978 func BenchmarkAllocateAll_64_80(b *testing.B) { benchmarkAllocateAllIPv6("2001:db8::/64", 80, b) }