1package structs 2 3import ( 4 "fmt" 5 "net" 6 "reflect" 7 "testing" 8 9 "github.com/stretchr/testify/require" 10) 11 12func TestNetworkIndex_Overcommitted(t *testing.T) { 13 t.Skip() 14 idx := NewNetworkIndex() 15 16 // Consume some network 17 reserved := &NetworkResource{ 18 Device: "eth0", 19 IP: "192.168.0.100", 20 MBits: 505, 21 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 22 } 23 collide := idx.AddReserved(reserved) 24 if collide { 25 t.Fatalf("bad") 26 } 27 if !idx.Overcommitted() { 28 t.Fatalf("have no resources") 29 } 30 31 // Add resources 32 n := &Node{ 33 NodeResources: &NodeResources{ 34 Networks: []*NetworkResource{ 35 { 36 Device: "eth0", 37 CIDR: "192.168.0.100/32", 38 MBits: 1000, 39 }, 40 }, 41 }, 42 } 43 idx.SetNode(n) 44 if idx.Overcommitted() { 45 t.Fatalf("have resources") 46 } 47 48 // Double up our usage 49 idx.AddReserved(reserved) 50 if !idx.Overcommitted() { 51 t.Fatalf("should be overcommitted") 52 } 53} 54 55func TestNetworkIndex_SetNode(t *testing.T) { 56 idx := NewNetworkIndex() 57 n := &Node{ 58 NodeResources: &NodeResources{ 59 Networks: []*NetworkResource{ 60 { 61 Device: "eth0", 62 CIDR: "192.168.0.100/32", 63 IP: "192.168.0.100", 64 MBits: 1000, 65 }, 66 }, 67 }, 68 ReservedResources: &NodeReservedResources{ 69 Networks: NodeReservedNetworkResources{ 70 ReservedHostPorts: "22", 71 }, 72 }, 73 } 74 collide := idx.SetNode(n) 75 if collide { 76 t.Fatalf("bad") 77 } 78 79 if len(idx.AvailNetworks) != 1 { 80 t.Fatalf("Bad") 81 } 82 if idx.AvailBandwidth["eth0"] != 1000 { 83 t.Fatalf("Bad") 84 } 85 if !idx.UsedPorts["192.168.0.100"].Check(22) { 86 t.Fatalf("Bad") 87 } 88} 89 90func TestNetworkIndex_AddAllocs(t *testing.T) { 91 idx := NewNetworkIndex() 92 allocs := []*Allocation{ 93 { 94 AllocatedResources: &AllocatedResources{ 95 Tasks: map[string]*AllocatedTaskResources{ 96 "web": { 97 Networks: []*NetworkResource{ 98 { 99 Device: "eth0", 100 IP: "192.168.0.100", 101 MBits: 20, 102 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 103 }, 104 }, 105 }, 106 }, 107 }, 108 }, 109 { 110 AllocatedResources: &AllocatedResources{ 111 Tasks: map[string]*AllocatedTaskResources{ 112 "api": { 113 Networks: []*NetworkResource{ 114 { 115 Device: "eth0", 116 IP: "192.168.0.100", 117 MBits: 50, 118 ReservedPorts: []Port{{"one", 10000, 0, ""}}, 119 }, 120 }, 121 }, 122 }, 123 }, 124 }, 125 } 126 collide := idx.AddAllocs(allocs) 127 if collide { 128 t.Fatalf("bad") 129 } 130 131 if idx.UsedBandwidth["eth0"] != 70 { 132 t.Fatalf("Bad") 133 } 134 if !idx.UsedPorts["192.168.0.100"].Check(8000) { 135 t.Fatalf("Bad") 136 } 137 if !idx.UsedPorts["192.168.0.100"].Check(9000) { 138 t.Fatalf("Bad") 139 } 140 if !idx.UsedPorts["192.168.0.100"].Check(10000) { 141 t.Fatalf("Bad") 142 } 143} 144 145func TestNetworkIndex_AddReserved(t *testing.T) { 146 idx := NewNetworkIndex() 147 148 reserved := &NetworkResource{ 149 Device: "eth0", 150 IP: "192.168.0.100", 151 MBits: 20, 152 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 153 } 154 collide := idx.AddReserved(reserved) 155 if collide { 156 t.Fatalf("bad") 157 } 158 159 if idx.UsedBandwidth["eth0"] != 20 { 160 t.Fatalf("Bad") 161 } 162 if !idx.UsedPorts["192.168.0.100"].Check(8000) { 163 t.Fatalf("Bad") 164 } 165 if !idx.UsedPorts["192.168.0.100"].Check(9000) { 166 t.Fatalf("Bad") 167 } 168 169 // Try to reserve the same network 170 collide = idx.AddReserved(reserved) 171 if !collide { 172 t.Fatalf("bad") 173 } 174} 175 176// XXX Reserving ports doesn't work when yielding from a CIDR block. This is 177// okay for now since we do not actually fingerprint CIDR blocks. 178func TestNetworkIndex_yieldIP(t *testing.T) { 179 idx := NewNetworkIndex() 180 n := &Node{ 181 NodeResources: &NodeResources{ 182 Networks: []*NetworkResource{ 183 { 184 Device: "eth0", 185 CIDR: "192.168.0.100/30", 186 MBits: 1000, 187 }, 188 }, 189 }, 190 } 191 idx.SetNode(n) 192 193 var out []string 194 idx.yieldIP(func(n *NetworkResource, ip net.IP) (stop bool) { 195 out = append(out, ip.String()) 196 return 197 }) 198 199 expect := []string{"192.168.0.100", "192.168.0.101", 200 "192.168.0.102", "192.168.0.103"} 201 if !reflect.DeepEqual(out, expect) { 202 t.Fatalf("bad: %v", out) 203 } 204} 205 206func TestNetworkIndex_AssignNetwork(t *testing.T) { 207 idx := NewNetworkIndex() 208 n := &Node{ 209 NodeResources: &NodeResources{ 210 Networks: []*NetworkResource{ 211 { 212 Device: "eth0", 213 CIDR: "192.168.0.100/30", 214 MBits: 1000, 215 }, 216 }, 217 }, 218 } 219 idx.SetNode(n) 220 221 allocs := []*Allocation{ 222 { 223 TaskResources: map[string]*Resources{ 224 "web": { 225 Networks: []*NetworkResource{ 226 { 227 Device: "eth0", 228 IP: "192.168.0.100", 229 MBits: 20, 230 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 231 }, 232 }, 233 }, 234 }, 235 }, 236 { 237 TaskResources: map[string]*Resources{ 238 "api": { 239 Networks: []*NetworkResource{ 240 { 241 Device: "eth0", 242 IP: "192.168.0.100", 243 MBits: 50, 244 ReservedPorts: []Port{{"main", 10000, 0, ""}}, 245 }, 246 }, 247 }, 248 }, 249 }, 250 } 251 idx.AddAllocs(allocs) 252 253 // Ask for a reserved port 254 ask := &NetworkResource{ 255 ReservedPorts: []Port{{"main", 8000, 0, ""}}, 256 } 257 offer, err := idx.AssignNetwork(ask) 258 require.NoError(t, err) 259 require.NotNil(t, offer) 260 require.Equal(t, "192.168.0.101", offer.IP) 261 rp := Port{"main", 8000, 0, ""} 262 require.Len(t, offer.ReservedPorts, 1) 263 require.Exactly(t, rp, offer.ReservedPorts[0]) 264 265 // Ask for dynamic ports 266 ask = &NetworkResource{ 267 DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, -1, ""}}, 268 } 269 offer, err = idx.AssignNetwork(ask) 270 require.NoError(t, err) 271 require.NotNil(t, offer) 272 require.Equal(t, "192.168.0.100", offer.IP) 273 require.Len(t, offer.DynamicPorts, 3) 274 var adminPort Port 275 for _, port := range offer.DynamicPorts { 276 require.NotZero(t, port.Value) 277 if port.Label == "admin" { 278 adminPort = port 279 } 280 } 281 require.Equal(t, adminPort.Value, adminPort.To) 282 283 // Ask for reserved + dynamic ports 284 ask = &NetworkResource{ 285 ReservedPorts: []Port{{"main", 2345, 0, ""}}, 286 DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, 8080, ""}}, 287 } 288 offer, err = idx.AssignNetwork(ask) 289 require.NoError(t, err) 290 require.NotNil(t, offer) 291 require.Equal(t, "192.168.0.100", offer.IP) 292 293 rp = Port{"main", 2345, 0, ""} 294 require.Len(t, offer.ReservedPorts, 1) 295 require.Exactly(t, rp, offer.ReservedPorts[0]) 296 297 // Ask for too much bandwidth 298 ask = &NetworkResource{ 299 MBits: 1000, 300 } 301 offer, err = idx.AssignNetwork(ask) 302 require.Error(t, err) 303 require.Equal(t, "bandwidth exceeded", err.Error()) 304 require.Nil(t, offer) 305} 306 307// This test ensures that even with a small domain of available ports we are 308// able to make a dynamic port allocation. 309func TestNetworkIndex_AssignNetwork_Dynamic_Contention(t *testing.T) { 310 311 // Create a node that only has one free port 312 idx := NewNetworkIndex() 313 n := &Node{ 314 NodeResources: &NodeResources{ 315 Networks: []*NetworkResource{ 316 { 317 Device: "eth0", 318 CIDR: "192.168.0.100/32", 319 IP: "192.168.0.100", 320 MBits: 1000, 321 }, 322 }, 323 }, 324 ReservedResources: &NodeReservedResources{ 325 Networks: NodeReservedNetworkResources{ 326 ReservedHostPorts: fmt.Sprintf("%d-%d", MinDynamicPort, MaxDynamicPort-1), 327 }, 328 }, 329 } 330 idx.SetNode(n) 331 332 // Ask for dynamic ports 333 ask := &NetworkResource{ 334 DynamicPorts: []Port{{"http", 0, 80, ""}}, 335 } 336 offer, err := idx.AssignNetwork(ask) 337 if err != nil { 338 t.Fatalf("err: %v", err) 339 } 340 if offer == nil { 341 t.Fatalf("bad") 342 } 343 if offer.IP != "192.168.0.100" { 344 t.Fatalf("bad: %#v", offer) 345 } 346 if len(offer.DynamicPorts) != 1 { 347 t.Fatalf("There should be one dynamic ports") 348 } 349 if p := offer.DynamicPorts[0].Value; p != MaxDynamicPort { 350 t.Fatalf("Dynamic Port: should have been assigned %d; got %d", p, MaxDynamicPort) 351 } 352} 353 354// COMPAT(0.11): Remove in 0.11 355func TestNetworkIndex_SetNode_Old(t *testing.T) { 356 idx := NewNetworkIndex() 357 n := &Node{ 358 Resources: &Resources{ 359 Networks: []*NetworkResource{ 360 { 361 Device: "eth0", 362 CIDR: "192.168.0.100/32", 363 MBits: 1000, 364 }, 365 }, 366 }, 367 Reserved: &Resources{ 368 Networks: []*NetworkResource{ 369 { 370 Device: "eth0", 371 IP: "192.168.0.100", 372 ReservedPorts: []Port{{"ssh", 22, 0, ""}}, 373 MBits: 1, 374 }, 375 }, 376 }, 377 } 378 collide := idx.SetNode(n) 379 if collide { 380 t.Fatalf("bad") 381 } 382 383 if len(idx.AvailNetworks) != 1 { 384 t.Fatalf("Bad") 385 } 386 if idx.AvailBandwidth["eth0"] != 1000 { 387 t.Fatalf("Bad") 388 } 389 if idx.UsedBandwidth["eth0"] != 1 { 390 t.Fatalf("Bad") 391 } 392 if !idx.UsedPorts["192.168.0.100"].Check(22) { 393 t.Fatalf("Bad") 394 } 395} 396 397// COMPAT(0.11): Remove in 0.11 398func TestNetworkIndex_AddAllocs_Old(t *testing.T) { 399 idx := NewNetworkIndex() 400 allocs := []*Allocation{ 401 { 402 TaskResources: map[string]*Resources{ 403 "web": { 404 Networks: []*NetworkResource{ 405 { 406 Device: "eth0", 407 IP: "192.168.0.100", 408 MBits: 20, 409 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 410 }, 411 }, 412 }, 413 }, 414 }, 415 { 416 TaskResources: map[string]*Resources{ 417 "api": { 418 Networks: []*NetworkResource{ 419 { 420 Device: "eth0", 421 IP: "192.168.0.100", 422 MBits: 50, 423 ReservedPorts: []Port{{"one", 10000, 0, ""}}, 424 }, 425 }, 426 }, 427 }, 428 }, 429 } 430 collide := idx.AddAllocs(allocs) 431 if collide { 432 t.Fatalf("bad") 433 } 434 435 if idx.UsedBandwidth["eth0"] != 70 { 436 t.Fatalf("Bad") 437 } 438 if !idx.UsedPorts["192.168.0.100"].Check(8000) { 439 t.Fatalf("Bad") 440 } 441 if !idx.UsedPorts["192.168.0.100"].Check(9000) { 442 t.Fatalf("Bad") 443 } 444 if !idx.UsedPorts["192.168.0.100"].Check(10000) { 445 t.Fatalf("Bad") 446 } 447} 448 449// COMPAT(0.11): Remove in 0.11 450func TestNetworkIndex_yieldIP_Old(t *testing.T) { 451 idx := NewNetworkIndex() 452 n := &Node{ 453 Resources: &Resources{ 454 Networks: []*NetworkResource{ 455 { 456 Device: "eth0", 457 CIDR: "192.168.0.100/30", 458 MBits: 1000, 459 }, 460 }, 461 }, 462 Reserved: &Resources{ 463 Networks: []*NetworkResource{ 464 { 465 Device: "eth0", 466 IP: "192.168.0.100", 467 ReservedPorts: []Port{{"ssh", 22, 0, ""}}, 468 MBits: 1, 469 }, 470 }, 471 }, 472 } 473 idx.SetNode(n) 474 475 var out []string 476 idx.yieldIP(func(n *NetworkResource, ip net.IP) (stop bool) { 477 out = append(out, ip.String()) 478 return 479 }) 480 481 expect := []string{"192.168.0.100", "192.168.0.101", 482 "192.168.0.102", "192.168.0.103"} 483 if !reflect.DeepEqual(out, expect) { 484 t.Fatalf("bad: %v", out) 485 } 486} 487 488// COMPAT(0.11): Remove in 0.11 489func TestNetworkIndex_AssignNetwork_Old(t *testing.T) { 490 idx := NewNetworkIndex() 491 n := &Node{ 492 Resources: &Resources{ 493 Networks: []*NetworkResource{ 494 { 495 Device: "eth0", 496 CIDR: "192.168.0.100/30", 497 MBits: 1000, 498 }, 499 }, 500 }, 501 Reserved: &Resources{ 502 Networks: []*NetworkResource{ 503 { 504 Device: "eth0", 505 IP: "192.168.0.100", 506 ReservedPorts: []Port{{"ssh", 22, 0, ""}}, 507 MBits: 1, 508 }, 509 }, 510 }, 511 } 512 idx.SetNode(n) 513 514 allocs := []*Allocation{ 515 { 516 TaskResources: map[string]*Resources{ 517 "web": { 518 Networks: []*NetworkResource{ 519 { 520 Device: "eth0", 521 IP: "192.168.0.100", 522 MBits: 20, 523 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 524 }, 525 }, 526 }, 527 }, 528 }, 529 { 530 TaskResources: map[string]*Resources{ 531 "api": { 532 Networks: []*NetworkResource{ 533 { 534 Device: "eth0", 535 IP: "192.168.0.100", 536 MBits: 50, 537 ReservedPorts: []Port{{"main", 10000, 0, ""}}, 538 }, 539 }, 540 }, 541 }, 542 }, 543 } 544 idx.AddAllocs(allocs) 545 546 // Ask for a reserved port 547 ask := &NetworkResource{ 548 ReservedPorts: []Port{{"main", 8000, 0, ""}}, 549 } 550 offer, err := idx.AssignNetwork(ask) 551 if err != nil { 552 t.Fatalf("err: %v", err) 553 } 554 if offer == nil { 555 t.Fatalf("bad") 556 } 557 if offer.IP != "192.168.0.101" { 558 t.Fatalf("bad: %#v", offer) 559 } 560 rp := Port{"main", 8000, 0, ""} 561 if len(offer.ReservedPorts) != 1 || offer.ReservedPorts[0] != rp { 562 t.Fatalf("bad: %#v", offer) 563 } 564 565 // Ask for dynamic ports 566 ask = &NetworkResource{ 567 DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, 8080, ""}}, 568 } 569 offer, err = idx.AssignNetwork(ask) 570 if err != nil { 571 t.Fatalf("err: %v", err) 572 } 573 if offer == nil { 574 t.Fatalf("bad") 575 } 576 if offer.IP != "192.168.0.100" { 577 t.Fatalf("bad: %#v", offer) 578 } 579 if len(offer.DynamicPorts) != 3 { 580 t.Fatalf("There should be three dynamic ports") 581 } 582 for _, port := range offer.DynamicPorts { 583 if port.Value == 0 { 584 t.Fatalf("Dynamic Port: %v should have been assigned a host port", port.Label) 585 } 586 } 587 588 // Ask for reserved + dynamic ports 589 ask = &NetworkResource{ 590 ReservedPorts: []Port{{"main", 2345, 0, ""}}, 591 DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, 8080, ""}}, 592 } 593 offer, err = idx.AssignNetwork(ask) 594 if err != nil { 595 t.Fatalf("err: %v", err) 596 } 597 if offer == nil { 598 t.Fatalf("bad") 599 } 600 if offer.IP != "192.168.0.100" { 601 t.Fatalf("bad: %#v", offer) 602 } 603 604 rp = Port{"main", 2345, 0, ""} 605 if len(offer.ReservedPorts) != 1 || offer.ReservedPorts[0] != rp { 606 t.Fatalf("bad: %#v", offer) 607 } 608 609 // Ask for too much bandwidth 610 ask = &NetworkResource{ 611 MBits: 1000, 612 } 613 offer, err = idx.AssignNetwork(ask) 614 if err.Error() != "bandwidth exceeded" { 615 t.Fatalf("err: %v", err) 616 } 617 if offer != nil { 618 t.Fatalf("bad") 619 } 620} 621 622// COMPAT(0.11): Remove in 0.11 623// This test ensures that even with a small domain of available ports we are 624// able to make a dynamic port allocation. 625func TestNetworkIndex_AssignNetwork_Dynamic_Contention_Old(t *testing.T) { 626 627 // Create a node that only has one free port 628 idx := NewNetworkIndex() 629 n := &Node{ 630 Resources: &Resources{ 631 Networks: []*NetworkResource{ 632 { 633 Device: "eth0", 634 CIDR: "192.168.0.100/32", 635 MBits: 1000, 636 }, 637 }, 638 }, 639 Reserved: &Resources{ 640 Networks: []*NetworkResource{ 641 { 642 Device: "eth0", 643 IP: "192.168.0.100", 644 MBits: 1, 645 }, 646 }, 647 }, 648 } 649 for i := MinDynamicPort; i < MaxDynamicPort; i++ { 650 n.Reserved.Networks[0].ReservedPorts = append(n.Reserved.Networks[0].ReservedPorts, Port{Value: i}) 651 } 652 653 idx.SetNode(n) 654 655 // Ask for dynamic ports 656 ask := &NetworkResource{ 657 DynamicPorts: []Port{{"http", 0, 80, ""}}, 658 } 659 offer, err := idx.AssignNetwork(ask) 660 if err != nil { 661 t.Fatalf("err: %v", err) 662 } 663 if offer == nil { 664 t.Fatalf("bad") 665 } 666 if offer.IP != "192.168.0.100" { 667 t.Fatalf("bad: %#v", offer) 668 } 669 if len(offer.DynamicPorts) != 1 { 670 t.Fatalf("There should be three dynamic ports") 671 } 672 if p := offer.DynamicPorts[0].Value; p != MaxDynamicPort { 673 t.Fatalf("Dynamic Port: should have been assigned %d; got %d", p, MaxDynamicPort) 674 } 675} 676 677func TestIntContains(t *testing.T) { 678 l := []int{1, 2, 10, 20} 679 if isPortReserved(l, 50) { 680 t.Fatalf("bad") 681 } 682 if !isPortReserved(l, 20) { 683 t.Fatalf("bad") 684 } 685 if !isPortReserved(l, 1) { 686 t.Fatalf("bad") 687 } 688} 689