github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/libnetwork/ipam/allocator_test.go (about) 1 package ipam 2 3 import ( 4 "context" 5 "flag" 6 "fmt" 7 "math/rand" 8 "net" 9 "net/netip" 10 "runtime" 11 "strconv" 12 "sync" 13 "testing" 14 "time" 15 16 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/bitmap" 17 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/ipamapi" 18 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/ipamutils" 19 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/types" 20 "golang.org/x/sync/errgroup" 21 "gotest.tools/v3/assert" 22 is "gotest.tools/v3/assert/cmp" 23 ) 24 25 func TestKeyString(t *testing.T) { 26 k := &PoolID{AddressSpace: "default", SubnetKey: SubnetKey{Subnet: netip.MustParsePrefix("172.27.0.0/16")}} 27 expected := "default/172.27.0.0/16" 28 if expected != k.String() { 29 t.Fatalf("Unexpected key string: %s", k.String()) 30 } 31 32 k2, err := PoolIDFromString(expected) 33 if err != nil { 34 t.Fatal(err) 35 } 36 if k2.AddressSpace != k.AddressSpace || k2.Subnet != k.Subnet { 37 t.Fatalf("SubnetKey.FromString() failed. Expected %v. Got %v", k, k2) 38 } 39 40 expected = fmt.Sprintf("%s/%s", expected, "172.27.3.0/24") 41 k.ChildSubnet = netip.MustParsePrefix("172.27.3.0/24") 42 if expected != k.String() { 43 t.Fatalf("Unexpected key string: %s", k.String()) 44 } 45 46 k2, err = PoolIDFromString(expected) 47 if err != nil { 48 t.Fatal(err) 49 } 50 if k2.AddressSpace != k.AddressSpace || k2.Subnet != k.Subnet || k2.ChildSubnet != k.ChildSubnet { 51 t.Fatalf("SubnetKey.FromString() failed. Expected %v. Got %v", k, k2) 52 } 53 } 54 55 func TestAddSubnets(t *testing.T) { 56 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 57 if err != nil { 58 t.Fatal(err) 59 } 60 61 pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) 62 if err != nil { 63 t.Fatal("Unexpected failure in adding subnet") 64 } 65 66 pid1, _, _, err := a.RequestPool(globalAddressSpace, "10.0.0.0/8", "", nil, false) 67 if err != nil { 68 t.Fatalf("Unexpected failure in adding overlapping subnets to different address spaces: %v", err) 69 } 70 71 if pid0 == pid1 { 72 t.Fatal("returned same pool id for same subnets in different namespaces") 73 } 74 75 _, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "", nil, false) 76 if err == nil { 77 t.Fatalf("Expected failure requesting existing subnet") 78 } 79 80 _, _, _, err = a.RequestPool(globalAddressSpace, "10.128.0.0/9", "", nil, false) 81 if err == nil { 82 t.Fatal("Expected failure on adding overlapping base subnet") 83 } 84 85 _, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "10.128.0.0/9", nil, false) 86 if err != nil { 87 t.Fatalf("Unexpected failure on adding sub pool: %v", err) 88 } 89 _, _, _, err = a.RequestPool(globalAddressSpace, "10.0.0.0/8", "10.128.0.0/9", nil, false) 90 if err == nil { 91 t.Fatalf("Expected failure on adding overlapping sub pool") 92 } 93 94 _, _, _, err = a.RequestPool(localAddressSpace, "10.20.2.0/24", "", nil, false) 95 if err == nil { 96 t.Fatal("Failed to detect overlapping subnets") 97 } 98 99 _, _, _, err = a.RequestPool(localAddressSpace, "10.128.0.0/9", "", nil, false) 100 if err == nil { 101 t.Fatal("Failed to detect overlapping subnets") 102 } 103 104 _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3:4:5:6::/112", "", nil, false) 105 if err != nil { 106 t.Fatalf("Failed to add v6 subnet: %s", err.Error()) 107 } 108 109 _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3::/64", "", nil, false) 110 if err == nil { 111 t.Fatal("Failed to detect overlapping v6 subnet") 112 } 113 } 114 115 // TestDoublePoolRelease tests that releasing a pool which has already 116 // been released raises an error. 117 func TestDoublePoolRelease(t *testing.T) { 118 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 119 assert.NilError(t, err) 120 121 pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) 122 assert.NilError(t, err) 123 124 err = a.ReleasePool(pid0) 125 assert.NilError(t, err) 126 127 err = a.ReleasePool(pid0) 128 assert.Check(t, is.ErrorContains(err, "")) 129 } 130 131 func TestAddReleasePoolID(t *testing.T) { 132 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 133 assert.NilError(t, err) 134 135 _, err = a.getAddrSpace(localAddressSpace) 136 if err != nil { 137 t.Fatal(err) 138 } 139 140 pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) 141 if err != nil { 142 t.Fatalf("Unexpected failure in adding pool: %v", err) 143 } 144 k0, err := PoolIDFromString(pid0) 145 if err != nil { 146 t.Fatal(err) 147 } 148 149 aSpace, err := a.getAddrSpace(localAddressSpace) 150 if err != nil { 151 t.Fatal(err) 152 } 153 154 if got := aSpace.subnets[k0.Subnet].autoRelease; got != false { 155 t.Errorf("Unexpected autoRelease value for %s: %v", k0, got) 156 } 157 158 pid1, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) 159 if err != nil { 160 t.Fatalf("Unexpected failure in adding sub pool: %v", err) 161 } 162 k1, err := PoolIDFromString(pid1) 163 if err != nil { 164 t.Fatal(err) 165 } 166 167 if pid0 == pid1 { 168 t.Fatalf("Incorrect poolIDs returned %s, %s", pid0, pid1) 169 } 170 171 aSpace, err = a.getAddrSpace(localAddressSpace) 172 if err != nil { 173 t.Fatal(err) 174 } 175 176 if got := aSpace.subnets[k1.Subnet].autoRelease; got != false { 177 t.Errorf("Unexpected autoRelease value for %s: %v", k1, got) 178 } 179 180 _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) 181 if err == nil { 182 t.Fatalf("Expected failure in adding sub pool: %v", err) 183 } 184 185 aSpace, err = a.getAddrSpace(localAddressSpace) 186 if err != nil { 187 t.Fatal(err) 188 } 189 190 if got := aSpace.subnets[k0.Subnet].autoRelease; got != false { 191 t.Errorf("Unexpected autoRelease value for %s: %v", k0, got) 192 } 193 194 if err := a.ReleasePool(pid1); err != nil { 195 t.Fatal(err) 196 } 197 198 aSpace, err = a.getAddrSpace(localAddressSpace) 199 if err != nil { 200 t.Fatal(err) 201 } 202 203 if got := aSpace.subnets[k0.Subnet].autoRelease; got != false { 204 t.Errorf("Unexpected autoRelease value for %s: %v", k0, got) 205 } 206 if err := a.ReleasePool(pid0); err != nil { 207 t.Error(err) 208 } 209 210 if _, ok := aSpace.subnets[k0.Subnet]; ok { 211 t.Error("Pool should have been deleted when released") 212 } 213 214 pid00, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) 215 if err != nil { 216 t.Errorf("Unexpected failure in adding pool: %v", err) 217 } 218 if pid00 != pid0 { 219 t.Errorf("main pool should still exist. Got poolID %q, want %q", pid00, pid0) 220 } 221 222 aSpace, err = a.getAddrSpace(localAddressSpace) 223 if err != nil { 224 t.Fatal(err) 225 } 226 227 if got := aSpace.subnets[k0.Subnet].autoRelease; got != false { 228 t.Errorf("Unexpected autoRelease value for %s: %v", k0, got) 229 } 230 231 if err := a.ReleasePool(pid00); err != nil { 232 t.Error(err) 233 } 234 235 aSpace, err = a.getAddrSpace(localAddressSpace) 236 if err != nil { 237 t.Fatal(err) 238 } 239 240 if bp, ok := aSpace.subnets[k0.Subnet]; ok { 241 t.Errorf("Base pool %s is still present: %v", k0, bp) 242 } 243 244 _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) 245 if err != nil { 246 t.Errorf("Unexpected failure in adding pool: %v", err) 247 } 248 249 aSpace, err = a.getAddrSpace(localAddressSpace) 250 if err != nil { 251 t.Fatal(err) 252 } 253 254 if got := aSpace.subnets[k0.Subnet].autoRelease; got != false { 255 t.Errorf("Unexpected autoRelease value for %s: %v", k0, got) 256 } 257 } 258 259 func TestPredefinedPool(t *testing.T) { 260 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 261 assert.NilError(t, err) 262 263 pid, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) 264 if err != nil { 265 t.Fatal(err) 266 } 267 268 pid2, nw2, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) 269 if err != nil { 270 t.Fatal(err) 271 } 272 273 if types.CompareIPNet(nw, nw2) { 274 t.Fatalf("Unexpected default network returned: %s = %s", nw2, nw) 275 } 276 277 if err := a.ReleasePool(pid); err != nil { 278 t.Fatal(err) 279 } 280 281 if err := a.ReleasePool(pid2); err != nil { 282 t.Fatal(err) 283 } 284 } 285 286 func TestRemoveSubnet(t *testing.T) { 287 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 288 assert.NilError(t, err) 289 290 input := []struct { 291 addrSpace string 292 subnet string 293 v6 bool 294 }{ 295 {localAddressSpace, "192.168.0.0/16", false}, 296 {localAddressSpace, "172.17.0.0/16", false}, 297 {localAddressSpace, "10.0.0.0/8", false}, 298 {localAddressSpace, "2001:db8:1:2:3:4:ffff::/112", false}, 299 {globalAddressSpace, "172.17.0.0/16", false}, 300 {globalAddressSpace, "10.0.0.0/8", false}, 301 {globalAddressSpace, "2001:db8:1:2:3:4:5::/112", true}, 302 {globalAddressSpace, "2001:db8:1:2:3:4:ffff::/112", true}, 303 } 304 305 poolIDs := make([]string, len(input)) 306 307 for ind, i := range input { 308 if poolIDs[ind], _, _, err = a.RequestPool(i.addrSpace, i.subnet, "", nil, i.v6); err != nil { 309 t.Fatalf("Failed to apply input. Can't proceed: %s", err.Error()) 310 } 311 } 312 313 for ind, id := range poolIDs { 314 if err := a.ReleasePool(id); err != nil { 315 t.Fatalf("Failed to release poolID %s (%d)", id, ind) 316 } 317 } 318 } 319 320 func TestGetSameAddress(t *testing.T) { 321 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 322 assert.NilError(t, err) 323 324 pid, _, _, err := a.RequestPool(localAddressSpace, "192.168.100.0/24", "", nil, false) 325 if err != nil { 326 t.Fatal(err) 327 } 328 329 ip := net.ParseIP("192.168.100.250") 330 _, _, err = a.RequestAddress(pid, ip, nil) 331 if err != nil { 332 t.Fatal(err) 333 } 334 335 _, _, err = a.RequestAddress(pid, ip, nil) 336 if err == nil { 337 t.Fatal(err) 338 } 339 } 340 341 func TestPoolAllocationReuse(t *testing.T) { 342 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 343 assert.NilError(t, err) 344 345 // First get all pools until they are exhausted to 346 pList := []string{} 347 pool, _, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) 348 for err == nil { 349 pList = append(pList, pool) 350 pool, _, _, err = a.RequestPool(localAddressSpace, "", "", nil, false) 351 } 352 nPools := len(pList) 353 for _, pool := range pList { 354 if err := a.ReleasePool(pool); err != nil { 355 t.Fatal(err) 356 } 357 } 358 359 // Now try to allocate then free nPool pools sequentially. 360 // Verify that we don't see any repeat networks even though 361 // we have freed them. 362 seen := map[string]bool{} 363 for i := 0; i < nPools; i++ { 364 pool, nw, _, err := a.RequestPool(localAddressSpace, "", "", nil, false) 365 if err != nil { 366 t.Fatal(err) 367 } 368 if _, ok := seen[nw.String()]; ok { 369 t.Fatalf("Network %s was reused before exhausing the pool list", nw.String()) 370 } 371 seen[nw.String()] = true 372 if err := a.ReleasePool(pool); err != nil { 373 t.Fatal(err) 374 } 375 } 376 } 377 378 func TestGetAddressSubPoolEqualPool(t *testing.T) { 379 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 380 assert.NilError(t, err) 381 382 // Requesting a subpool of same size of the master pool should not cause any problem on ip allocation 383 pid, _, _, err := a.RequestPool(localAddressSpace, "172.18.0.0/16", "172.18.0.0/16", nil, false) 384 if err != nil { 385 t.Fatal(err) 386 } 387 388 _, _, err = a.RequestAddress(pid, nil, nil) 389 if err != nil { 390 t.Fatal(err) 391 } 392 } 393 394 func TestRequestReleaseAddressFromSubPool(t *testing.T) { 395 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 396 assert.NilError(t, err) 397 398 poolID, _, _, err := a.RequestPool(localAddressSpace, "172.28.0.0/16", "172.28.30.0/24", nil, false) 399 if err != nil { 400 t.Fatal(err) 401 } 402 403 var ip *net.IPNet 404 expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}} 405 for err == nil { 406 var c *net.IPNet 407 if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil { 408 ip = c 409 } 410 } 411 if err != ipamapi.ErrNoAvailableIPs { 412 t.Fatal(err) 413 } 414 if !types.CompareIPNet(expected, ip) { 415 t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) 416 } 417 rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}} 418 if err = a.ReleaseAddress(poolID, rp.IP); err != nil { 419 t.Fatal(err) 420 } 421 if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil { 422 t.Fatal(err) 423 } 424 if !types.CompareIPNet(rp, ip) { 425 t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) 426 } 427 428 _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) 429 if err != nil { 430 t.Fatal(err) 431 } 432 poolID, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/16", "10.0.0.0/24", nil, false) 433 if err != nil { 434 t.Fatal(err) 435 } 436 expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}} 437 for err == nil { 438 var c *net.IPNet 439 if c, _, err = a.RequestAddress(poolID, nil, nil); err == nil { 440 ip = c 441 } 442 } 443 if err != ipamapi.ErrNoAvailableIPs { 444 t.Fatal(err) 445 } 446 if !types.CompareIPNet(expected, ip) { 447 t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) 448 } 449 rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}} 450 if err = a.ReleaseAddress(poolID, rp.IP); err != nil { 451 t.Fatal(err) 452 } 453 if ip, _, err = a.RequestAddress(poolID, nil, nil); err != nil { 454 t.Fatal(err) 455 } 456 if !types.CompareIPNet(rp, ip) { 457 t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) 458 } 459 460 // Request any addresses from subpool after explicit address request 461 unoExp, _ := types.ParseCIDR("10.2.2.0/16") 462 dueExp, _ := types.ParseCIDR("10.2.2.2/16") 463 treExp, _ := types.ParseCIDR("10.2.2.1/16") 464 465 if poolID, _, _, err = a.RequestPool(localAddressSpace, "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil { 466 t.Fatal(err) 467 } 468 tre, _, err := a.RequestAddress(poolID, treExp.IP, nil) 469 if err != nil { 470 t.Fatal(err) 471 } 472 if !types.CompareIPNet(tre, treExp) { 473 t.Fatalf("Unexpected address: want %v, got %v", treExp, tre) 474 } 475 476 uno, _, err := a.RequestAddress(poolID, nil, nil) 477 if err != nil { 478 t.Fatal(err) 479 } 480 if !types.CompareIPNet(uno, unoExp) { 481 t.Fatalf("Unexpected address: %v", uno) 482 } 483 484 due, _, err := a.RequestAddress(poolID, nil, nil) 485 if err != nil { 486 t.Fatal(err) 487 } 488 if !types.CompareIPNet(due, dueExp) { 489 t.Fatalf("Unexpected address: %v", due) 490 } 491 492 if err = a.ReleaseAddress(poolID, uno.IP); err != nil { 493 t.Fatal(err) 494 } 495 uno, _, err = a.RequestAddress(poolID, nil, nil) 496 if err != nil { 497 t.Fatal(err) 498 } 499 if !types.CompareIPNet(uno, unoExp) { 500 t.Fatalf("Unexpected address: %v", uno) 501 } 502 503 if err = a.ReleaseAddress(poolID, tre.IP); err != nil { 504 t.Fatal(err) 505 } 506 tre, _, err = a.RequestAddress(poolID, nil, nil) 507 if err != nil { 508 t.Fatal(err) 509 } 510 if !types.CompareIPNet(tre, treExp) { 511 t.Fatalf("Unexpected address: %v", tre) 512 } 513 } 514 515 func TestSerializeRequestReleaseAddressFromSubPool(t *testing.T) { 516 opts := map[string]string{ 517 ipamapi.AllocSerialPrefix: "true", 518 } 519 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 520 assert.NilError(t, err) 521 522 poolID, _, _, err := a.RequestPool(localAddressSpace, "172.28.0.0/16", "172.28.30.0/24", nil, false) 523 if err != nil { 524 t.Fatal(err) 525 } 526 527 var ip *net.IPNet 528 expected := &net.IPNet{IP: net.IP{172, 28, 30, 255}, Mask: net.IPMask{255, 255, 0, 0}} 529 for err == nil { 530 var c *net.IPNet 531 if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { 532 ip = c 533 } 534 } 535 if err != ipamapi.ErrNoAvailableIPs { 536 t.Fatal(err) 537 } 538 if !types.CompareIPNet(expected, ip) { 539 t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) 540 } 541 rp := &net.IPNet{IP: net.IP{172, 28, 30, 97}, Mask: net.IPMask{255, 255, 0, 0}} 542 if err = a.ReleaseAddress(poolID, rp.IP); err != nil { 543 t.Fatal(err) 544 } 545 if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil { 546 t.Fatal(err) 547 } 548 if !types.CompareIPNet(rp, ip) { 549 t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) 550 } 551 552 _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) 553 if err != nil { 554 t.Fatal(err) 555 } 556 poolID, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/16", "10.0.0.0/24", nil, false) 557 if err != nil { 558 t.Fatal(err) 559 } 560 expected = &net.IPNet{IP: net.IP{10, 0, 0, 255}, Mask: net.IPMask{255, 255, 0, 0}} 561 for err == nil { 562 var c *net.IPNet 563 if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { 564 ip = c 565 } 566 } 567 if err != ipamapi.ErrNoAvailableIPs { 568 t.Fatal(err) 569 } 570 if !types.CompareIPNet(expected, ip) { 571 t.Fatalf("Unexpected last IP from subpool. Expected: %s. Got: %v.", expected, ip) 572 } 573 rp = &net.IPNet{IP: net.IP{10, 0, 0, 79}, Mask: net.IPMask{255, 255, 0, 0}} 574 if err = a.ReleaseAddress(poolID, rp.IP); err != nil { 575 t.Fatal(err) 576 } 577 if ip, _, err = a.RequestAddress(poolID, nil, opts); err != nil { 578 t.Fatal(err) 579 } 580 if !types.CompareIPNet(rp, ip) { 581 t.Fatalf("Unexpected IP from subpool. Expected: %s. Got: %v.", rp, ip) 582 } 583 584 // Request any addresses from subpool after explicit address request 585 unoExp, _ := types.ParseCIDR("10.2.2.0/16") 586 dueExp, _ := types.ParseCIDR("10.2.2.2/16") 587 treExp, _ := types.ParseCIDR("10.2.2.1/16") 588 quaExp, _ := types.ParseCIDR("10.2.2.3/16") 589 fivExp, _ := types.ParseCIDR("10.2.2.4/16") 590 if poolID, _, _, err = a.RequestPool(localAddressSpace, "10.2.0.0/16", "10.2.2.0/24", nil, false); err != nil { 591 t.Fatal(err) 592 } 593 tre, _, err := a.RequestAddress(poolID, treExp.IP, opts) 594 if err != nil { 595 t.Fatal(err) 596 } 597 if !types.CompareIPNet(tre, treExp) { 598 t.Fatalf("Unexpected address: want %v, got %v", treExp, tre) 599 } 600 601 uno, _, err := a.RequestAddress(poolID, nil, opts) 602 if err != nil { 603 t.Fatal(err) 604 } 605 if !types.CompareIPNet(uno, unoExp) { 606 t.Fatalf("Unexpected address: %v", uno) 607 } 608 609 due, _, err := a.RequestAddress(poolID, nil, opts) 610 if err != nil { 611 t.Fatal(err) 612 } 613 if !types.CompareIPNet(due, dueExp) { 614 t.Fatalf("Unexpected address: %v", due) 615 } 616 617 if err = a.ReleaseAddress(poolID, uno.IP); err != nil { 618 t.Fatal(err) 619 } 620 uno, _, err = a.RequestAddress(poolID, nil, opts) 621 if err != nil { 622 t.Fatal(err) 623 } 624 if !types.CompareIPNet(uno, quaExp) { 625 t.Fatalf("Unexpected address: %v", uno) 626 } 627 628 if err = a.ReleaseAddress(poolID, tre.IP); err != nil { 629 t.Fatal(err) 630 } 631 tre, _, err = a.RequestAddress(poolID, nil, opts) 632 if err != nil { 633 t.Fatal(err) 634 } 635 if !types.CompareIPNet(tre, fivExp) { 636 t.Fatalf("Unexpected address: %v", tre) 637 } 638 } 639 640 func TestGetAddress(t *testing.T) { 641 input := []string{ 642 /*"10.0.0.0/8", "10.0.0.0/9", "10.0.0.0/10",*/ "10.0.0.0/11", "10.0.0.0/12", "10.0.0.0/13", "10.0.0.0/14", 643 "10.0.0.0/15", "10.0.0.0/16", "10.0.0.0/17", "10.0.0.0/18", "10.0.0.0/19", "10.0.0.0/20", "10.0.0.0/21", 644 "10.0.0.0/22", "10.0.0.0/23", "10.0.0.0/24", "10.0.0.0/25", "10.0.0.0/26", "10.0.0.0/27", "10.0.0.0/28", 645 "10.0.0.0/29", "10.0.0.0/30", "10.0.0.0/31", 646 } 647 648 for _, subnet := range input { 649 assertGetAddress(t, subnet) 650 } 651 } 652 653 func TestRequestSyntaxCheck(t *testing.T) { 654 var ( 655 pool = "192.168.0.0/16" 656 subPool = "192.168.0.0/24" 657 ) 658 659 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 660 assert.NilError(t, err) 661 662 _, _, _, err = a.RequestPool("", pool, "", nil, false) 663 if err == nil { 664 t.Fatal("Failed to detect wrong request: empty address space") 665 } 666 667 _, _, _, err = a.RequestPool("", pool, subPool, nil, false) 668 if err == nil { 669 t.Fatal("Failed to detect wrong request: empty address space") 670 } 671 672 _, _, _, err = a.RequestPool(localAddressSpace, "", subPool, nil, false) 673 if err == nil { 674 t.Fatal("Failed to detect wrong request: subPool specified and no pool") 675 } 676 677 pid, _, _, err := a.RequestPool(localAddressSpace, pool, subPool, nil, false) 678 if err != nil { 679 t.Fatalf("Unexpected failure: %v", err) 680 } 681 682 _, _, err = a.RequestAddress("", nil, nil) 683 if err == nil { 684 t.Fatal("Failed to detect wrong request: no pool id specified") 685 } 686 687 ip := net.ParseIP("172.17.0.23") 688 _, _, err = a.RequestAddress(pid, ip, nil) 689 if err == nil { 690 t.Fatal("Failed to detect wrong request: requested IP from different subnet") 691 } 692 693 ip = net.ParseIP("192.168.0.50") 694 _, _, err = a.RequestAddress(pid, ip, nil) 695 if err != nil { 696 t.Fatalf("Unexpected failure: %v", err) 697 } 698 699 err = a.ReleaseAddress("", ip) 700 if err == nil { 701 t.Fatal("Failed to detect wrong request: no pool id specified") 702 } 703 704 err = a.ReleaseAddress(pid, nil) 705 if err == nil { 706 t.Fatal("Failed to detect wrong request: no pool id specified") 707 } 708 709 err = a.ReleaseAddress(pid, ip) 710 if err != nil { 711 t.Fatalf("Unexpected failure: %v: %s, %s", err, pid, ip) 712 } 713 } 714 715 func TestRequest(t *testing.T) { 716 // Request N addresses from different size subnets, verifying last request 717 // returns expected address. Internal subnet host size is Allocator's default, 16 718 input := []struct { 719 subnet string 720 numReq int 721 lastIP string 722 }{ 723 {"192.168.59.0/24", 254, "192.168.59.254"}, 724 {"192.168.240.0/20", 255, "192.168.240.255"}, 725 {"192.168.0.0/16", 255, "192.168.0.255"}, 726 {"192.168.0.0/16", 256, "192.168.1.0"}, 727 {"10.16.0.0/16", 255, "10.16.0.255"}, 728 {"10.128.0.0/12", 255, "10.128.0.255"}, 729 {"10.0.0.0/8", 256, "10.0.1.0"}, 730 731 {"192.168.128.0/18", 4*256 - 1, "192.168.131.255"}, 732 /* 733 {"192.168.240.0/20", 16*256 - 2, "192.168.255.254"}, 734 735 {"192.168.0.0/16", 256*256 - 2, "192.168.255.254"}, 736 {"10.0.0.0/8", 2 * 256, "10.0.2.0"}, 737 {"10.0.0.0/8", 5 * 256, "10.0.5.0"}, 738 {"10.0.0.0/8", 100 * 256 * 254, "10.99.255.254"}, 739 */ 740 } 741 742 for _, d := range input { 743 assertNRequests(t, d.subnet, d.numReq, d.lastIP) 744 } 745 } 746 747 // TestOverlappingRequests tests that overlapping subnets cannot be allocated. 748 // Requests for subnets which are supersets or subsets of existing allocations, 749 // or which overlap at the beginning or end, should not be permitted. 750 func TestOverlappingRequests(t *testing.T) { 751 input := []struct { 752 environment []string 753 subnet string 754 ok bool 755 }{ 756 // IPv4 757 // Previously allocated network does not overlap with request 758 {[]string{"10.0.0.0/8"}, "11.0.0.0/8", true}, 759 {[]string{"74.0.0.0/7"}, "9.111.99.72/30", true}, 760 {[]string{"110.192.0.0/10"}, "16.0.0.0/10", true}, 761 762 // Previously allocated network entirely contains request 763 {[]string{"10.0.0.0/8"}, "10.0.0.0/8", false}, // exact overlap 764 {[]string{"0.0.0.0/1"}, "16.182.0.0/15", false}, 765 {[]string{"16.0.0.0/4"}, "17.11.66.0/23", false}, 766 767 // Previously allocated network overlaps beginning of request 768 {[]string{"0.0.0.0/1"}, "0.0.0.0/0", false}, 769 {[]string{"64.0.0.0/6"}, "64.0.0.0/3", false}, 770 {[]string{"112.0.0.0/6"}, "112.0.0.0/4", false}, 771 772 // Previously allocated network overlaps end of request 773 {[]string{"96.0.0.0/3"}, "0.0.0.0/1", false}, 774 {[]string{"192.0.0.0/2"}, "128.0.0.0/1", false}, 775 {[]string{"95.0.0.0/8"}, "92.0.0.0/6", false}, 776 777 // Previously allocated network entirely contained within request 778 {[]string{"10.0.0.0/8"}, "10.0.0.0/6", false}, // non-canonical 779 {[]string{"10.0.0.0/8"}, "8.0.0.0/6", false}, // canonical 780 {[]string{"25.173.144.0/20"}, "0.0.0.0/0", false}, 781 782 // IPv6 783 // Previously allocated network entirely contains request 784 {[]string{"::/0"}, "f656:3484:c878:a05:e540:a6ed:4d70:3740/123", false}, 785 {[]string{"8000::/1"}, "8fe8:e7c4:5779::/49", false}, 786 {[]string{"f000::/4"}, "ffc7:6000::/19", false}, 787 788 // Previously allocated network overlaps beginning of request 789 {[]string{"::/2"}, "::/0", false}, 790 {[]string{"::/3"}, "::/1", false}, 791 {[]string{"::/6"}, "::/5", false}, 792 793 // Previously allocated network overlaps end of request 794 {[]string{"c000::/2"}, "8000::/1", false}, 795 {[]string{"7c00::/6"}, "::/1", false}, 796 {[]string{"cf80::/9"}, "c000::/4", false}, 797 798 // Previously allocated network entirely contained within request 799 {[]string{"ff77:93f8::/29"}, "::/0", false}, 800 {[]string{"9287:2e20:5134:fab6:9061:a0c6:bfe3:9400/119"}, "8000::/1", false}, 801 {[]string{"3ea1:bfa9:8691:d1c6:8c46:519b:db6d:e700/120"}, "3000::/4", false}, 802 } 803 804 for _, tc := range input { 805 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 806 assert.NilError(t, err) 807 808 // Set up some existing allocations. This should always succeed. 809 for _, env := range tc.environment { 810 _, _, _, err = a.RequestPool(localAddressSpace, env, "", nil, false) 811 assert.NilError(t, err) 812 } 813 814 // Make the test allocation. 815 _, _, _, err = a.RequestPool(localAddressSpace, tc.subnet, "", nil, false) 816 if tc.ok { 817 assert.NilError(t, err) 818 } else { 819 assert.Check(t, is.ErrorContains(err, "")) 820 } 821 } 822 } 823 824 func TestUnusualSubnets(t *testing.T) { 825 subnet := "192.168.0.2/31" 826 827 outsideTheRangeAddresses := []struct { 828 address string 829 }{ 830 {"192.168.0.1"}, 831 {"192.168.0.4"}, 832 {"192.168.0.100"}, 833 } 834 835 expectedAddresses := []struct { 836 address string 837 }{ 838 {"192.168.0.2"}, 839 {"192.168.0.3"}, 840 } 841 842 allocator, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 843 if err != nil { 844 t.Fatal(err) 845 } 846 847 // 848 // IPv4 /31 blocks. See RFC 3021. 849 // 850 851 pool, _, _, err := allocator.RequestPool(localAddressSpace, subnet, "", nil, false) 852 if err != nil { 853 t.Fatal(err) 854 } 855 856 // Outside-the-range 857 858 for _, outside := range outsideTheRangeAddresses { 859 _, _, errx := allocator.RequestAddress(pool, net.ParseIP(outside.address), nil) 860 if errx != ipamapi.ErrIPOutOfRange { 861 t.Fatalf("Address %s failed to throw expected error: %s", outside.address, errx.Error()) 862 } 863 } 864 865 // Should get just these two IPs followed by exhaustion on the next request 866 867 for _, expected := range expectedAddresses { 868 got, _, errx := allocator.RequestAddress(pool, nil, nil) 869 if errx != nil { 870 t.Fatalf("Failed to obtain the address: %s", errx.Error()) 871 } 872 expectedIP := net.ParseIP(expected.address) 873 gotIP := got.IP 874 if !gotIP.Equal(expectedIP) { 875 t.Fatalf("Failed to obtain sequentialaddress. Expected: %s, Got: %s", expectedIP, gotIP) 876 } 877 } 878 879 _, _, err = allocator.RequestAddress(pool, nil, nil) 880 if err != ipamapi.ErrNoAvailableIPs { 881 t.Fatal("Did not get expected error when pool is exhausted.") 882 } 883 } 884 885 func TestRelease(t *testing.T) { 886 subnet := "192.168.0.0/23" 887 888 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 889 assert.NilError(t, err) 890 891 pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) 892 if err != nil { 893 t.Fatal(err) 894 } 895 896 // Allocate all addresses 897 for err != ipamapi.ErrNoAvailableIPs { 898 _, _, err = a.RequestAddress(pid, nil, nil) 899 } 900 901 toRelease := []struct { 902 address string 903 }{ 904 {"192.168.0.1"}, 905 {"192.168.0.2"}, 906 {"192.168.0.3"}, 907 {"192.168.0.4"}, 908 {"192.168.0.5"}, 909 {"192.168.0.6"}, 910 {"192.168.0.7"}, 911 {"192.168.0.8"}, 912 {"192.168.0.9"}, 913 {"192.168.0.10"}, 914 {"192.168.0.30"}, 915 {"192.168.0.31"}, 916 {"192.168.1.32"}, 917 918 {"192.168.0.254"}, 919 {"192.168.1.1"}, 920 {"192.168.1.2"}, 921 922 {"192.168.1.3"}, 923 924 {"192.168.1.253"}, 925 {"192.168.1.254"}, 926 } 927 928 // One by one, release the address and request again. We should get the same IP 929 for i, inp := range toRelease { 930 ip0 := net.ParseIP(inp.address) 931 a.ReleaseAddress(pid, ip0) 932 bm := a.local.subnets[netip.MustParsePrefix(subnet)].addrs 933 if bm.Unselected() != 1 { 934 t.Fatalf("Failed to update free address count after release. Expected %d, Found: %d", i+1, bm.Unselected()) 935 } 936 937 nw, _, err := a.RequestAddress(pid, nil, nil) 938 if err != nil { 939 t.Fatalf("Failed to obtain the address: %s", err.Error()) 940 } 941 ip := nw.IP 942 if !ip0.Equal(ip) { 943 t.Fatalf("Failed to obtain the same address. Expected: %s, Got: %s", ip0, ip) 944 } 945 } 946 } 947 948 func assertGetAddress(t *testing.T, subnet string) { 949 var ( 950 err error 951 printTime = false 952 ) 953 954 sub := netip.MustParsePrefix(subnet) 955 ones, bits := sub.Bits(), sub.Addr().BitLen() 956 zeroes := bits - ones 957 numAddresses := 1 << uint(zeroes) 958 959 bm := bitmap.New(uint64(numAddresses)) 960 961 start := time.Now() 962 run := 0 963 for err != ipamapi.ErrNoAvailableIPs { 964 _, err = getAddress(sub, bm, netip.Addr{}, netip.Prefix{}, false) 965 run++ 966 } 967 if printTime { 968 fmt.Printf("\nTaken %v, to allocate all addresses on %s. (nemAddresses: %d. Runs: %d)", time.Since(start), subnet, numAddresses, run) 969 } 970 if bm.Unselected() != 0 { 971 t.Fatalf("Unexpected free count after reserving all addresses: %d", bm.Unselected()) 972 } 973 /* 974 if bm.Head.Block != expectedMax || bm.Head.Count != numBlocks { 975 t.Fatalf("Failed to effectively reserve all addresses on %s. Expected (0x%x, %d) as first sequence. Found (0x%x,%d)", 976 subnet, expectedMax, numBlocks, bm.Head.Block, bm.Head.Count) 977 } 978 */ 979 } 980 981 func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP string) { 982 var ( 983 nw *net.IPNet 984 printTime = false 985 ) 986 987 lastIP := net.ParseIP(lastExpectedIP) 988 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 989 assert.NilError(t, err) 990 991 pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) 992 if err != nil { 993 t.Fatal(err) 994 } 995 996 i := 0 997 start := time.Now() 998 for ; i < numReq; i++ { 999 nw, _, err = a.RequestAddress(pid, nil, nil) 1000 if err != nil { 1001 t.Fatal(err) 1002 } 1003 } 1004 if printTime { 1005 fmt.Printf("\nTaken %v, to allocate %d addresses on %s\n", time.Since(start), numReq, subnet) 1006 } 1007 1008 if !lastIP.Equal(nw.IP) { 1009 t.Fatalf("Wrong last IP. Expected %s. Got: %s (err: %v, ind: %d)", lastExpectedIP, nw.IP.String(), err, i) 1010 } 1011 } 1012 1013 func benchmarkRequest(b *testing.B, a *Allocator, subnet string) { 1014 pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) 1015 for err != ipamapi.ErrNoAvailableIPs { 1016 _, _, err = a.RequestAddress(pid, nil, nil) 1017 } 1018 } 1019 1020 func BenchmarkRequest(b *testing.B) { 1021 subnets := []string{ 1022 "10.0.0.0/24", 1023 "10.0.0.0/16", 1024 "10.0.0.0/8", 1025 } 1026 1027 for _, subnet := range subnets { 1028 name := fmt.Sprintf("%vSubnet", subnet) 1029 b.Run(name, func(b *testing.B) { 1030 a, _ := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 1031 benchmarkRequest(b, a, subnet) 1032 }) 1033 } 1034 } 1035 1036 func TestAllocateRandomDeallocate(t *testing.T) { 1037 for _, store := range []bool{false, true} { 1038 testAllocateRandomDeallocate(t, "172.25.0.0/16", "", 384, store) 1039 testAllocateRandomDeallocate(t, "172.25.0.0/16", "172.25.252.0/22", 384, store) 1040 } 1041 } 1042 1043 func testAllocateRandomDeallocate(t *testing.T, pool, subPool string, num int, store bool) { 1044 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 1045 if err != nil { 1046 t.Fatal(err) 1047 } 1048 1049 pid, _, _, err := a.RequestPool(localAddressSpace, pool, subPool, nil, false) 1050 if err != nil { 1051 t.Fatal(err) 1052 } 1053 1054 // Allocate num ip addresses 1055 indices := make(map[int]*net.IPNet, num) 1056 allocated := make(map[string]bool, num) 1057 for i := 0; i < num; i++ { 1058 ip, _, err := a.RequestAddress(pid, nil, nil) 1059 if err != nil { 1060 t.Fatal(err) 1061 } 1062 ips := ip.String() 1063 if _, ok := allocated[ips]; ok { 1064 t.Fatalf("Address %s is already allocated", ips) 1065 } 1066 allocated[ips] = true 1067 indices[i] = ip 1068 } 1069 if len(indices) != len(allocated) || len(indices) != num { 1070 t.Fatalf("Unexpected number of allocated addresses: (%d,%d).", len(indices), len(allocated)) 1071 } 1072 1073 seed := time.Now().Unix() 1074 rng := rand.New(rand.NewSource(seed)) 1075 1076 // Deallocate half of the allocated addresses following a random pattern 1077 pattern := rng.Perm(num) 1078 for i := 0; i < num/2; i++ { 1079 idx := pattern[i] 1080 ip := indices[idx] 1081 err := a.ReleaseAddress(pid, ip.IP) 1082 if err != nil { 1083 t.Fatalf("Unexpected failure on deallocation of %s: %v.\nSeed: %d.", ip, err, seed) 1084 } 1085 delete(indices, idx) 1086 delete(allocated, ip.String()) 1087 } 1088 1089 // Request a quarter of addresses 1090 for i := 0; i < num/2; i++ { 1091 ip, _, err := a.RequestAddress(pid, nil, nil) 1092 if err != nil { 1093 t.Fatal(err) 1094 } 1095 ips := ip.String() 1096 if _, ok := allocated[ips]; ok { 1097 t.Fatalf("\nAddress %s is already allocated.\nSeed: %d.", ips, seed) 1098 } 1099 allocated[ips] = true 1100 } 1101 if len(allocated) != num { 1102 t.Fatalf("Unexpected number of allocated addresses: %d.\nSeed: %d.", len(allocated), seed) 1103 } 1104 } 1105 1106 const ( 1107 numInstances = 5 1108 first = 0 1109 ) 1110 1111 var ( 1112 allocator *Allocator 1113 start = make(chan struct{}) 1114 done sync.WaitGroup 1115 pools = make([]*net.IPNet, numInstances) 1116 ) 1117 1118 func runParallelTests(t *testing.T, instance int) { 1119 var err error 1120 1121 t.Parallel() 1122 1123 pTest := flag.Lookup("test.parallel") 1124 if pTest == nil { 1125 t.Skip("Skipped because test.parallel flag not set;") 1126 } 1127 numParallel, err := strconv.Atoi(pTest.Value.String()) 1128 if err != nil { 1129 t.Fatal(err) 1130 } 1131 if numParallel < numInstances { 1132 t.Skip("Skipped because t.parallel was less than ", numInstances) 1133 } 1134 1135 // The first instance creates the allocator, gives the start 1136 // and finally checks the pools each instance was assigned 1137 if instance == first { 1138 allocator, err = NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 1139 if err != nil { 1140 t.Fatal(err) 1141 } 1142 done.Add(numInstances - 1) 1143 close(start) 1144 } 1145 1146 if instance != first { 1147 <-start 1148 defer done.Done() 1149 } 1150 1151 _, pools[instance], _, err = allocator.RequestPool(localAddressSpace, "", "", nil, false) 1152 if err != nil { 1153 t.Fatal(err) 1154 } 1155 1156 if instance == first { 1157 done.Wait() 1158 // Now check each instance got a different pool 1159 for i := 0; i < numInstances; i++ { 1160 for j := i + 1; j < numInstances; j++ { 1161 if types.CompareIPNet(pools[i], pools[j]) { 1162 t.Errorf("Instance %d and %d were given the same predefined pool: %v", i, j, pools) 1163 } 1164 } 1165 } 1166 } 1167 } 1168 1169 func TestRequestReleaseAddressDuplicate(t *testing.T) { 1170 a, err := NewAllocator(ipamutils.GetLocalScopeDefaultNetworks(), ipamutils.GetGlobalScopeDefaultNetworks()) 1171 if err != nil { 1172 t.Fatal(err) 1173 } 1174 type IP struct { 1175 ip *net.IPNet 1176 ref int 1177 } 1178 ips := []IP{} 1179 allocatedIPs := []*net.IPNet{} 1180 1181 opts := map[string]string{ 1182 ipamapi.AllocSerialPrefix: "true", 1183 } 1184 var l sync.Mutex 1185 1186 poolID, _, _, err := a.RequestPool(localAddressSpace, "198.168.0.0/23", "", nil, false) 1187 if err != nil { 1188 t.Fatal(err) 1189 } 1190 1191 seed := time.Now().Unix() 1192 t.Logf("Random seed: %v", seed) 1193 rng := rand.New(rand.NewSource(seed)) 1194 1195 group, ctx := errgroup.WithContext(context.Background()) 1196 outer: 1197 for n := 0; n < 10000; n++ { 1198 var c *net.IPNet 1199 for { 1200 select { 1201 case <-ctx.Done(): 1202 // One of group's goroutines returned an error. 1203 break outer 1204 default: 1205 } 1206 if c, _, err = a.RequestAddress(poolID, nil, opts); err == nil { 1207 break 1208 } 1209 // No addresses available. Spin until one is. 1210 runtime.Gosched() 1211 } 1212 l.Lock() 1213 ips = append(ips, IP{c, 1}) 1214 l.Unlock() 1215 allocatedIPs = append(allocatedIPs, c) 1216 if len(allocatedIPs) > 500 { 1217 i := rng.Intn(len(allocatedIPs) - 1) 1218 ip := allocatedIPs[i] 1219 allocatedIPs = append(allocatedIPs[:i], allocatedIPs[i+1:]...) 1220 1221 group.Go(func() error { 1222 // The lifetime of an allocated address begins when RequestAddress returns, and 1223 // ends when ReleaseAddress is called. But we can't atomically call one of those 1224 // methods and append to the log (ips slice) without also synchronizing the 1225 // calls with each other. Synchronizing the calls would defeat the whole point 1226 // of this test, which is to race ReleaseAddress against RequestAddress. We have 1227 // no choice but to leave a small window of uncertainty open. Appending to the 1228 // log after ReleaseAddress returns would allow the next RequestAddress call to 1229 // race the log-release operation, which could result in the reallocate being 1230 // logged before the release, despite the release happening before the 1231 // reallocate: a false positive. Our only other option is to append the release 1232 // to the log before calling ReleaseAddress, leaving a small race window for 1233 // false negatives. False positives mean a flaky test, so let's err on the side 1234 // of false negatives. Eventually we'll get lucky with a true-positive test 1235 // failure or with Go's race detector if a concurrency bug exists. 1236 l.Lock() 1237 ips = append(ips, IP{ip, -1}) 1238 l.Unlock() 1239 return a.ReleaseAddress(poolID, ip.IP) 1240 }) 1241 } 1242 } 1243 1244 if err := group.Wait(); err != nil { 1245 t.Fatal(err) 1246 } 1247 1248 refMap := make(map[string]int) 1249 for _, ip := range ips { 1250 refMap[ip.ip.String()] = refMap[ip.ip.String()] + ip.ref 1251 if refMap[ip.ip.String()] < 0 { 1252 t.Fatalf("IP %s was previously released", ip.ip.String()) 1253 } 1254 if refMap[ip.ip.String()] > 1 { 1255 t.Fatalf("IP %s was previously allocated", ip.ip.String()) 1256 } 1257 } 1258 } 1259 1260 func TestParallelPredefinedRequest1(t *testing.T) { 1261 runParallelTests(t, 0) 1262 } 1263 1264 func TestParallelPredefinedRequest2(t *testing.T) { 1265 runParallelTests(t, 1) 1266 } 1267 1268 func TestParallelPredefinedRequest3(t *testing.T) { 1269 runParallelTests(t, 2) 1270 } 1271 1272 func TestParallelPredefinedRequest4(t *testing.T) { 1273 runParallelTests(t, 3) 1274 } 1275 1276 func TestParallelPredefinedRequest5(t *testing.T) { 1277 runParallelTests(t, 4) 1278 } 1279 1280 func BenchmarkPoolIDToString(b *testing.B) { 1281 const poolIDString = "default/172.27.0.0/16/172.27.3.0/24" 1282 k, err := PoolIDFromString(poolIDString) 1283 if err != nil { 1284 b.Fatal(err) 1285 } 1286 1287 b.ReportAllocs() 1288 for i := 0; i < b.N; i++ { 1289 _ = k.String() 1290 } 1291 } 1292 1293 func BenchmarkPoolIDFromString(b *testing.B) { 1294 const poolIDString = "default/172.27.0.0/16/172.27.3.0/24" 1295 1296 b.ReportAllocs() 1297 for i := 0; i < b.N; i++ { 1298 _, err := PoolIDFromString(poolIDString) 1299 if err != nil { 1300 b.Fatal(err) 1301 } 1302 } 1303 }