github.com/hernad/nomad@v1.6.112/nomad/structs/network_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package structs 5 6 import ( 7 "fmt" 8 "net" 9 "reflect" 10 "testing" 11 12 "github.com/hernad/nomad/ci" 13 "github.com/shoenig/test/must" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 func TestNetworkIndex_Copy(t *testing.T) { 19 ci.Parallel(t) 20 21 n := &Node{ 22 NodeResources: &NodeResources{ 23 Networks: []*NetworkResource{ 24 { 25 Device: "eth0", 26 CIDR: "192.168.0.100/32", 27 IP: "192.168.0.100", 28 MBits: 1000, 29 }, 30 }, 31 NodeNetworks: []*NodeNetworkResource{ 32 { 33 Mode: "host", 34 Device: "eth0", 35 Speed: 1000, 36 Addresses: []NodeNetworkAddress{ 37 { 38 Alias: "default", 39 Address: "192.168.0.100", 40 Family: NodeNetworkAF_IPv4, 41 }, 42 }, 43 }, 44 }, 45 }, 46 Reserved: &Resources{ 47 Networks: []*NetworkResource{ 48 { 49 Device: "eth0", 50 IP: "192.168.0.100", 51 ReservedPorts: []Port{{Label: "ssh", Value: 22}}, 52 MBits: 1, 53 }, 54 }, 55 }, 56 ReservedResources: &NodeReservedResources{ 57 Networks: NodeReservedNetworkResources{ 58 ReservedHostPorts: "22", 59 }, 60 }, 61 } 62 63 allocs := []*Allocation{ 64 { 65 AllocatedResources: &AllocatedResources{ 66 Tasks: map[string]*AllocatedTaskResources{ 67 "web": { 68 Networks: []*NetworkResource{ 69 { 70 Device: "eth0", 71 IP: "192.168.0.100", 72 MBits: 20, 73 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 74 }, 75 }, 76 }, 77 }, 78 }, 79 }, 80 { 81 AllocatedResources: &AllocatedResources{ 82 Tasks: map[string]*AllocatedTaskResources{ 83 "api": { 84 Networks: []*NetworkResource{ 85 { 86 Device: "eth0", 87 IP: "192.168.0.100", 88 MBits: 50, 89 ReservedPorts: []Port{{"one", 10000, 0, ""}}, 90 }, 91 }, 92 }, 93 }, 94 }, 95 }, 96 } 97 98 netIdx := NewNetworkIndex() 99 netIdx.SetNode(n) 100 netIdx.AddAllocs(allocs) 101 102 // Copy must be equal. 103 netIdxCopy := netIdx.Copy() 104 require.Equal(t, netIdx, netIdxCopy) 105 106 // Modifying copy should not affect original value. 107 n.NodeResources.Networks[0].Device = "eth1" 108 n.ReservedResources.Networks.ReservedHostPorts = "22,80" 109 allocs = append(allocs, &Allocation{ 110 AllocatedResources: &AllocatedResources{ 111 Tasks: map[string]*AllocatedTaskResources{ 112 "db": { 113 Networks: []*NetworkResource{ 114 { 115 Device: "eth1", 116 IP: "192.168.0.104", 117 MBits: 50, 118 ReservedPorts: []Port{{"one", 4567, 0, ""}}, 119 }, 120 }, 121 }, 122 }, 123 }, 124 }) 125 netIdxCopy.SetNode(n) 126 netIdxCopy.AddAllocs(allocs) 127 netIdxCopy.MinDynamicPort = 1000 128 netIdxCopy.MaxDynamicPort = 2000 129 require.NotEqual(t, netIdx, netIdxCopy) 130 } 131 132 func TestNetworkIndex_Overcommitted(t *testing.T) { 133 t.Skip() 134 ci.Parallel(t) 135 idx := NewNetworkIndex() 136 137 // Consume some network 138 reserved := &NetworkResource{ 139 Device: "eth0", 140 IP: "192.168.0.100", 141 MBits: 505, 142 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 143 } 144 collide, reasons := idx.AddReserved(reserved) 145 if collide || len(reasons) != 0 { 146 t.Fatalf("bad") 147 } 148 if !idx.Overcommitted() { 149 t.Fatalf("have no resources") 150 } 151 152 // Add resources 153 n := &Node{ 154 NodeResources: &NodeResources{ 155 Networks: []*NetworkResource{ 156 { 157 Device: "eth0", 158 CIDR: "192.168.0.100/32", 159 MBits: 1000, 160 }, 161 }, 162 }, 163 } 164 idx.SetNode(n) 165 if idx.Overcommitted() { 166 t.Fatalf("have resources") 167 } 168 169 // Double up our usage 170 idx.AddReserved(reserved) 171 if !idx.Overcommitted() { 172 t.Fatalf("should be overcommitted") 173 } 174 } 175 176 func TestNetworkIndex_SetNode(t *testing.T) { 177 ci.Parallel(t) 178 179 idx := NewNetworkIndex() 180 n := &Node{ 181 NodeResources: &NodeResources{ 182 Networks: []*NetworkResource{ 183 { 184 Device: "eth0", 185 CIDR: "192.168.0.100/32", 186 IP: "192.168.0.100", 187 MBits: 1000, 188 }, 189 }, 190 }, 191 ReservedResources: &NodeReservedResources{ 192 Networks: NodeReservedNetworkResources{ 193 ReservedHostPorts: "22", 194 }, 195 }, 196 } 197 require.NoError(t, idx.SetNode(n)) 198 require.Len(t, idx.TaskNetworks, 1) 199 require.Equal(t, 1000, idx.AvailBandwidth["eth0"]) 200 require.True(t, idx.UsedPorts["192.168.0.100"].Check(22)) 201 } 202 203 func TestNetworkIndex_AddAllocs(t *testing.T) { 204 ci.Parallel(t) 205 206 idx := NewNetworkIndex() 207 allocs := []*Allocation{ 208 { 209 ClientStatus: AllocClientStatusRunning, 210 DesiredStatus: AllocDesiredStatusRun, 211 AllocatedResources: &AllocatedResources{ 212 Tasks: map[string]*AllocatedTaskResources{ 213 "web": { 214 Networks: []*NetworkResource{ 215 { 216 Device: "eth0", 217 IP: "192.168.0.100", 218 MBits: 20, 219 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 220 }, 221 }, 222 }, 223 }, 224 }, 225 }, 226 { 227 ClientStatus: AllocClientStatusRunning, 228 DesiredStatus: AllocDesiredStatusRun, 229 AllocatedResources: &AllocatedResources{ 230 Tasks: map[string]*AllocatedTaskResources{ 231 "api": { 232 Networks: []*NetworkResource{ 233 { 234 Device: "eth0", 235 IP: "192.168.0.100", 236 MBits: 50, 237 ReservedPorts: []Port{{"one", 10000, 0, ""}}, 238 }, 239 }, 240 }, 241 }, 242 }, 243 }, 244 { 245 // Allocations running on clients should have their 246 // ports counted even if their DesiredStatus=stop 247 ClientStatus: AllocClientStatusRunning, 248 DesiredStatus: AllocDesiredStatusStop, 249 AllocatedResources: &AllocatedResources{ 250 Tasks: map[string]*AllocatedTaskResources{ 251 "api": { 252 Networks: []*NetworkResource{ 253 { 254 Device: "eth0", 255 IP: "192.168.0.100", 256 MBits: 50, 257 ReservedPorts: []Port{{"one", 10001, 0, ""}}, 258 }, 259 }, 260 }, 261 }, 262 }, 263 }, 264 { 265 // Allocations *not* running on clients should *not* 266 // have their ports counted even if their 267 // DesiredStatus=run 268 ClientStatus: AllocClientStatusFailed, 269 DesiredStatus: AllocDesiredStatusRun, 270 AllocatedResources: &AllocatedResources{ 271 Tasks: map[string]*AllocatedTaskResources{ 272 "api": { 273 Networks: []*NetworkResource{ 274 { 275 Device: "eth0", 276 IP: "192.168.0.100", 277 MBits: 50, 278 ReservedPorts: []Port{{"one", 10001, 0, ""}}, 279 }, 280 }, 281 }, 282 }, 283 }, 284 }, 285 } 286 collide, reason := idx.AddAllocs(allocs) 287 assert.False(t, collide) 288 assert.Empty(t, reason) 289 290 assert.True(t, idx.UsedPorts["192.168.0.100"].Check(8000)) 291 assert.True(t, idx.UsedPorts["192.168.0.100"].Check(9000)) 292 assert.True(t, idx.UsedPorts["192.168.0.100"].Check(10000)) 293 assert.True(t, idx.UsedPorts["192.168.0.100"].Check(10001)) 294 } 295 296 func TestNetworkIndex_AddReserved(t *testing.T) { 297 ci.Parallel(t) 298 299 idx := NewNetworkIndex() 300 301 reserved := &NetworkResource{ 302 Device: "eth0", 303 IP: "192.168.0.100", 304 MBits: 20, 305 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 306 } 307 collide, reasons := idx.AddReserved(reserved) 308 if collide || len(reasons) > 0 { 309 t.Fatalf("bad") 310 } 311 312 if idx.UsedBandwidth["eth0"] != 20 { 313 t.Fatalf("Bad") 314 } 315 if !idx.UsedPorts["192.168.0.100"].Check(8000) { 316 t.Fatalf("Bad") 317 } 318 if !idx.UsedPorts["192.168.0.100"].Check(9000) { 319 t.Fatalf("Bad") 320 } 321 322 // Try to reserve the same network 323 collide, reasons = idx.AddReserved(reserved) 324 if !collide || len(reasons) == 0 { 325 t.Fatalf("bad") 326 } 327 } 328 329 // XXX Reserving ports doesn't work when yielding from a CIDR block. This is 330 // okay for now since we do not actually fingerprint CIDR blocks. 331 func TestNetworkIndex_yieldIP(t *testing.T) { 332 ci.Parallel(t) 333 334 idx := NewNetworkIndex() 335 n := &Node{ 336 NodeResources: &NodeResources{ 337 Networks: []*NetworkResource{ 338 { 339 Device: "eth0", 340 CIDR: "192.168.0.100/30", 341 MBits: 1000, 342 }, 343 }, 344 }, 345 } 346 idx.SetNode(n) 347 348 var out []string 349 idx.yieldIP(func(n *NetworkResource, ip net.IP) (stop bool) { 350 out = append(out, ip.String()) 351 return 352 }) 353 354 expect := []string{"192.168.0.100", "192.168.0.101", 355 "192.168.0.102", "192.168.0.103"} 356 if !reflect.DeepEqual(out, expect) { 357 t.Fatalf("bad: %v", out) 358 } 359 } 360 361 // TestNetworkIndex_AssignPorts exercises assigning ports on group networks. 362 func TestNetworkIndex_AssignPorts(t *testing.T) { 363 ci.Parallel(t) 364 365 // Create a node that only two free dynamic ports 366 idx := NewNetworkIndex() 367 n := &Node{ 368 NodeResources: &NodeResources{ 369 Networks: []*NetworkResource{ 370 { 371 Device: "eth0", 372 CIDR: "192.168.0.100/32", 373 IP: "192.168.0.100", 374 MBits: 1000, 375 }, 376 }, 377 NodeNetworks: []*NodeNetworkResource{ 378 { 379 Mode: "host", 380 Device: "eth0", 381 Speed: 1000, 382 Addresses: []NodeNetworkAddress{ 383 { 384 Alias: "default", 385 Address: "192.168.0.100", 386 Family: NodeNetworkAF_IPv4, 387 }, 388 }, 389 }, 390 }, 391 }, 392 ReservedResources: &NodeReservedResources{ 393 Networks: NodeReservedNetworkResources{ 394 ReservedHostPorts: fmt.Sprintf("%d-%d", idx.MinDynamicPort, idx.MaxDynamicPort-2), 395 }, 396 }, 397 } 398 399 idx.SetNode(n) 400 401 // Ask for 2 dynamic ports 402 ask := &NetworkResource{ 403 ReservedPorts: []Port{{"static", 443, 443, "default"}}, 404 DynamicPorts: []Port{{"http", 0, 80, "default"}, {"admin", 0, 8080, "default"}}, 405 } 406 offer, err := idx.AssignPorts(ask) 407 must.NoError(t, err) 408 must.NotNil(t, offer, must.Sprint("did not get an offer")) 409 410 staticPortMapping, ok := offer.Get("static") 411 must.True(t, ok) 412 413 httpPortMapping, ok := offer.Get("http") 414 must.True(t, ok) 415 416 adminPortMapping, ok := offer.Get("admin") 417 must.True(t, ok) 418 419 must.NotEq(t, httpPortMapping.Value, adminPortMapping.Value, 420 must.Sprint("assigned dynamic ports must not conflict")) 421 422 must.Eq(t, 443, staticPortMapping.Value) 423 must.Between(t, idx.MaxDynamicPort-1, httpPortMapping.Value, idx.MaxDynamicPort) 424 must.Between(t, idx.MaxDynamicPort-1, adminPortMapping.Value, idx.MaxDynamicPort) 425 } 426 427 // TestNetworkIndex_AssignPorts_SmallRange exercises assigning ports on group 428 // networks with small dynamic port ranges configured 429 func TestNetworkIndex_AssignPortss_SmallRange(t *testing.T) { 430 ci.Parallel(t) 431 432 n := &Node{ 433 NodeResources: &NodeResources{ 434 NodeNetworks: []*NodeNetworkResource{ 435 { 436 Mode: "host", 437 Device: "eth0", 438 Speed: 1000, 439 Addresses: []NodeNetworkAddress{ 440 { 441 Alias: "default", 442 Address: "192.168.0.100", 443 Family: NodeNetworkAF_IPv4, 444 }, 445 }, 446 }, 447 }, 448 }, 449 } 450 451 testCases := []struct { 452 name string 453 min int 454 max int 455 ask []Port 456 expectErr string 457 }{ 458 { 459 name: "1 dynamic port avail and 1 port requested", 460 min: 20000, 461 max: 20000, 462 ask: []Port{{"http", 0, 80, "default"}}, 463 expectErr: "", 464 }, 465 { 466 name: "1 dynamic port avail and 2 ports requested", 467 min: 20000, 468 max: 20000, 469 ask: []Port{{"http", 0, 80, "default"}, {"admin", 0, 80, "default"}}, 470 expectErr: "dynamic port selection failed", 471 }, 472 { 473 name: "2 dynamic ports avail and 2 ports requested", 474 min: 20000, 475 max: 20001, 476 ask: []Port{{"http", 0, 80, "default"}, {"admin", 0, 80, "default"}}, 477 expectErr: "", 478 }, 479 } 480 481 for _, tc := range testCases { 482 483 idx := NewNetworkIndex() 484 idx.MinDynamicPort = tc.min 485 idx.MaxDynamicPort = tc.max 486 idx.SetNode(n) 487 488 ask := &NetworkResource{DynamicPorts: tc.ask} 489 offer, err := idx.AssignPorts(ask) 490 if tc.expectErr != "" { 491 must.EqError(t, err, tc.expectErr) 492 } else { 493 must.NoError(t, err) 494 must.NotNil(t, offer, must.Sprint("did not get an offer")) 495 496 for _, port := range tc.ask { 497 _, ok := offer.Get(port.Label) 498 must.True(t, ok) 499 } 500 } 501 } 502 503 } 504 505 func TestNetworkIndex_AssignTaskNetwork(t *testing.T) { 506 ci.Parallel(t) 507 idx := NewNetworkIndex() 508 n := &Node{ 509 NodeResources: &NodeResources{ 510 Networks: []*NetworkResource{ 511 { 512 Device: "eth0", 513 CIDR: "192.168.0.100/30", 514 MBits: 1000, 515 }, 516 }, 517 }, 518 } 519 idx.SetNode(n) 520 521 allocs := []*Allocation{ 522 { 523 TaskResources: map[string]*Resources{ 524 "web": { 525 Networks: []*NetworkResource{ 526 { 527 Device: "eth0", 528 IP: "192.168.0.100", 529 MBits: 20, 530 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 531 }, 532 }, 533 }, 534 }, 535 }, 536 { 537 TaskResources: map[string]*Resources{ 538 "api": { 539 Networks: []*NetworkResource{ 540 { 541 Device: "eth0", 542 IP: "192.168.0.100", 543 MBits: 50, 544 ReservedPorts: []Port{{"main", 10000, 0, ""}}, 545 }, 546 }, 547 }, 548 }, 549 }, 550 } 551 idx.AddAllocs(allocs) 552 553 // Ask for a reserved port 554 ask := &NetworkResource{ 555 ReservedPorts: []Port{{"main", 8000, 0, ""}}, 556 } 557 offer, err := idx.AssignTaskNetwork(ask) 558 require.NoError(t, err) 559 require.NotNil(t, offer) 560 require.Equal(t, "192.168.0.101", offer.IP) 561 rp := Port{"main", 8000, 0, ""} 562 require.Len(t, offer.ReservedPorts, 1) 563 require.Exactly(t, rp, offer.ReservedPorts[0]) 564 565 // Ask for dynamic ports 566 ask = &NetworkResource{ 567 DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, -1, ""}}, 568 } 569 offer, err = idx.AssignTaskNetwork(ask) 570 require.NoError(t, err) 571 require.NotNil(t, offer) 572 require.Equal(t, "192.168.0.100", offer.IP) 573 require.Len(t, offer.DynamicPorts, 3) 574 var adminPort Port 575 for _, port := range offer.DynamicPorts { 576 require.NotZero(t, port.Value) 577 if port.Label == "admin" { 578 adminPort = port 579 } 580 } 581 require.Equal(t, adminPort.Value, adminPort.To) 582 583 // Ask for reserved + dynamic ports 584 ask = &NetworkResource{ 585 ReservedPorts: []Port{{"main", 2345, 0, ""}}, 586 DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, 8080, ""}}, 587 } 588 offer, err = idx.AssignTaskNetwork(ask) 589 require.NoError(t, err) 590 require.NotNil(t, offer) 591 require.Equal(t, "192.168.0.100", offer.IP) 592 593 rp = Port{"main", 2345, 0, ""} 594 require.Len(t, offer.ReservedPorts, 1) 595 require.Exactly(t, rp, offer.ReservedPorts[0]) 596 597 // Ask for too much bandwidth 598 ask = &NetworkResource{ 599 MBits: 1000, 600 } 601 offer, err = idx.AssignTaskNetwork(ask) 602 require.Error(t, err) 603 require.Equal(t, "bandwidth exceeded", err.Error()) 604 require.Nil(t, offer) 605 } 606 607 // This test ensures that even with a small domain of available ports we are 608 // able to make a dynamic port allocation. 609 func TestNetworkIndex_AssignTaskNetwork_Dynamic_Contention(t *testing.T) { 610 ci.Parallel(t) 611 612 // Create a node that only has two free dynamic ports 613 idx := NewNetworkIndex() 614 n := &Node{ 615 NodeResources: &NodeResources{ 616 Networks: []*NetworkResource{ 617 { 618 Device: "eth0", 619 CIDR: "192.168.0.100/32", 620 IP: "192.168.0.100", 621 MBits: 1000, 622 }, 623 }, 624 }, 625 ReservedResources: &NodeReservedResources{ 626 Networks: NodeReservedNetworkResources{ 627 // leave only 2 available ports 628 ReservedHostPorts: fmt.Sprintf("%d-%d", idx.MinDynamicPort, idx.MaxDynamicPort-2), 629 }, 630 }, 631 } 632 633 idx.SetNode(n) 634 635 // Ask for 2 dynamic ports 636 ask := &NetworkResource{ 637 DynamicPorts: []Port{{"http", 0, 80, ""}, {"admin", 0, 443, ""}}, 638 } 639 offer, err := idx.AssignTaskNetwork(ask) 640 must.NoError(t, err) 641 must.NotNil(t, offer, must.Sprint("did not get an offer")) 642 must.Eq(t, "192.168.0.100", offer.IP) 643 must.Len(t, 2, offer.DynamicPorts, must.Sprint("There should be two dynamic ports")) 644 645 must.NotEq(t, offer.DynamicPorts[0].Value, offer.DynamicPorts[1].Value, 646 must.Sprint("assigned dynamic ports must not conflict")) 647 must.Between(t, idx.MaxDynamicPort-1, offer.DynamicPorts[0].Value, idx.MaxDynamicPort) 648 must.Between(t, idx.MaxDynamicPort-1, offer.DynamicPorts[1].Value, idx.MaxDynamicPort) 649 } 650 651 // COMPAT(0.11): Remove in 0.11 652 func TestNetworkIndex_SetNode_Old(t *testing.T) { 653 ci.Parallel(t) 654 655 idx := NewNetworkIndex() 656 n := &Node{ 657 Resources: &Resources{ 658 Networks: []*NetworkResource{ 659 { 660 Device: "eth0", 661 CIDR: "192.168.0.100/32", 662 MBits: 1000, 663 }, 664 }, 665 }, 666 Reserved: &Resources{ 667 Networks: []*NetworkResource{ 668 { 669 Device: "eth0", 670 IP: "192.168.0.100", 671 ReservedPorts: []Port{{"ssh", 22, 0, ""}}, 672 MBits: 1, 673 }, 674 }, 675 }, 676 } 677 require.NoError(t, idx.SetNode(n)) 678 require.Len(t, idx.TaskNetworks, 1) 679 require.Equal(t, 1000, idx.AvailBandwidth["eth0"]) 680 require.Equal(t, 1, idx.UsedBandwidth["eth0"]) 681 require.True(t, idx.UsedPorts["192.168.0.100"].Check(22)) 682 } 683 684 // COMPAT(0.11): Remove in 0.11 685 func TestNetworkIndex_AddAllocs_Old(t *testing.T) { 686 ci.Parallel(t) 687 688 idx := NewNetworkIndex() 689 allocs := []*Allocation{ 690 { 691 TaskResources: map[string]*Resources{ 692 "web": { 693 Networks: []*NetworkResource{ 694 { 695 Device: "eth0", 696 IP: "192.168.0.100", 697 MBits: 20, 698 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 699 }, 700 }, 701 }, 702 }, 703 }, 704 { 705 TaskResources: map[string]*Resources{ 706 "api": { 707 Networks: []*NetworkResource{ 708 { 709 Device: "eth0", 710 IP: "192.168.0.100", 711 MBits: 50, 712 ReservedPorts: []Port{{"one", 10000, 0, ""}}, 713 }, 714 }, 715 }, 716 }, 717 }, 718 } 719 collide, reason := idx.AddAllocs(allocs) 720 if collide || reason != "" { 721 t.Fatalf("bad") 722 } 723 724 if idx.UsedBandwidth["eth0"] != 70 { 725 t.Fatalf("Bad") 726 } 727 if !idx.UsedPorts["192.168.0.100"].Check(8000) { 728 t.Fatalf("Bad") 729 } 730 if !idx.UsedPorts["192.168.0.100"].Check(9000) { 731 t.Fatalf("Bad") 732 } 733 if !idx.UsedPorts["192.168.0.100"].Check(10000) { 734 t.Fatalf("Bad") 735 } 736 } 737 738 // COMPAT(0.11): Remove in 0.11 739 func TestNetworkIndex_yieldIP_Old(t *testing.T) { 740 ci.Parallel(t) 741 742 idx := NewNetworkIndex() 743 n := &Node{ 744 Resources: &Resources{ 745 Networks: []*NetworkResource{ 746 { 747 Device: "eth0", 748 CIDR: "192.168.0.100/30", 749 MBits: 1000, 750 }, 751 }, 752 }, 753 Reserved: &Resources{ 754 Networks: []*NetworkResource{ 755 { 756 Device: "eth0", 757 IP: "192.168.0.100", 758 ReservedPorts: []Port{{"ssh", 22, 0, ""}}, 759 MBits: 1, 760 }, 761 }, 762 }, 763 } 764 idx.SetNode(n) 765 766 var out []string 767 idx.yieldIP(func(n *NetworkResource, ip net.IP) (stop bool) { 768 out = append(out, ip.String()) 769 return 770 }) 771 772 expect := []string{"192.168.0.100", "192.168.0.101", 773 "192.168.0.102", "192.168.0.103"} 774 if !reflect.DeepEqual(out, expect) { 775 t.Fatalf("bad: %v", out) 776 } 777 } 778 779 // COMPAT(0.11): Remove in 0.11 780 func TestNetworkIndex_AssignTaskNetwork_Old(t *testing.T) { 781 ci.Parallel(t) 782 783 idx := NewNetworkIndex() 784 n := &Node{ 785 Resources: &Resources{ 786 Networks: []*NetworkResource{ 787 { 788 Device: "eth0", 789 CIDR: "192.168.0.100/30", 790 MBits: 1000, 791 }, 792 }, 793 }, 794 Reserved: &Resources{ 795 Networks: []*NetworkResource{ 796 { 797 Device: "eth0", 798 IP: "192.168.0.100", 799 ReservedPorts: []Port{{"ssh", 22, 0, ""}}, 800 MBits: 1, 801 }, 802 }, 803 }, 804 } 805 idx.SetNode(n) 806 807 allocs := []*Allocation{ 808 { 809 TaskResources: map[string]*Resources{ 810 "web": { 811 Networks: []*NetworkResource{ 812 { 813 Device: "eth0", 814 IP: "192.168.0.100", 815 MBits: 20, 816 ReservedPorts: []Port{{"one", 8000, 0, ""}, {"two", 9000, 0, ""}}, 817 }, 818 }, 819 }, 820 }, 821 }, 822 { 823 TaskResources: map[string]*Resources{ 824 "api": { 825 Networks: []*NetworkResource{ 826 { 827 Device: "eth0", 828 IP: "192.168.0.100", 829 MBits: 50, 830 ReservedPorts: []Port{{"main", 10000, 0, ""}}, 831 }, 832 }, 833 }, 834 }, 835 }, 836 } 837 idx.AddAllocs(allocs) 838 839 // Ask for a reserved port 840 ask := &NetworkResource{ 841 ReservedPorts: []Port{{"main", 8000, 0, ""}}, 842 } 843 offer, err := idx.AssignTaskNetwork(ask) 844 if err != nil { 845 t.Fatalf("err: %v", err) 846 } 847 if offer == nil { 848 t.Fatalf("bad") 849 } 850 if offer.IP != "192.168.0.101" { 851 t.Fatalf("bad: %#v", offer) 852 } 853 rp := Port{"main", 8000, 0, ""} 854 if len(offer.ReservedPorts) != 1 || offer.ReservedPorts[0] != rp { 855 t.Fatalf("bad: %#v", offer) 856 } 857 858 // Ask for dynamic ports 859 ask = &NetworkResource{ 860 DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, 8080, ""}}, 861 } 862 offer, err = idx.AssignTaskNetwork(ask) 863 if err != nil { 864 t.Fatalf("err: %v", err) 865 } 866 if offer == nil { 867 t.Fatalf("bad") 868 } 869 if offer.IP != "192.168.0.100" { 870 t.Fatalf("bad: %#v", offer) 871 } 872 if len(offer.DynamicPorts) != 3 { 873 t.Fatalf("There should be three dynamic ports") 874 } 875 for _, port := range offer.DynamicPorts { 876 if port.Value == 0 { 877 t.Fatalf("Dynamic Port: %v should have been assigned a host port", port.Label) 878 } 879 } 880 881 // Ask for reserved + dynamic ports 882 ask = &NetworkResource{ 883 ReservedPorts: []Port{{"main", 2345, 0, ""}}, 884 DynamicPorts: []Port{{"http", 0, 80, ""}, {"https", 0, 443, ""}, {"admin", 0, 8080, ""}}, 885 } 886 offer, err = idx.AssignTaskNetwork(ask) 887 if err != nil { 888 t.Fatalf("err: %v", err) 889 } 890 if offer == nil { 891 t.Fatalf("bad") 892 } 893 if offer.IP != "192.168.0.100" { 894 t.Fatalf("bad: %#v", offer) 895 } 896 897 rp = Port{"main", 2345, 0, ""} 898 if len(offer.ReservedPorts) != 1 || offer.ReservedPorts[0] != rp { 899 t.Fatalf("bad: %#v", offer) 900 } 901 902 // Ask for too much bandwidth 903 ask = &NetworkResource{ 904 MBits: 1000, 905 } 906 offer, err = idx.AssignTaskNetwork(ask) 907 if err.Error() != "bandwidth exceeded" { 908 t.Fatalf("err: %v", err) 909 } 910 if offer != nil { 911 t.Fatalf("bad") 912 } 913 } 914 915 // COMPAT(0.11): Remove in 0.11 916 // This test ensures that even with a small domain of available ports we are 917 // able to make a dynamic port allocation. 918 func TestNetworkIndex_AssignTaskNetwork_Dynamic_Contention_Old(t *testing.T) { 919 ci.Parallel(t) 920 921 // Create a node that only has one free port 922 idx := NewNetworkIndex() 923 n := &Node{ 924 Resources: &Resources{ 925 Networks: []*NetworkResource{ 926 { 927 Device: "eth0", 928 CIDR: "192.168.0.100/32", 929 MBits: 1000, 930 }, 931 }, 932 }, 933 Reserved: &Resources{ 934 Networks: []*NetworkResource{ 935 { 936 Device: "eth0", 937 IP: "192.168.0.100", 938 MBits: 1, 939 }, 940 }, 941 }, 942 } 943 for i := idx.MinDynamicPort; i < idx.MaxDynamicPort; i++ { 944 n.Reserved.Networks[0].ReservedPorts = append(n.Reserved.Networks[0].ReservedPorts, Port{Value: i}) 945 } 946 947 idx.SetNode(n) 948 949 // Ask for dynamic ports 950 ask := &NetworkResource{ 951 DynamicPorts: []Port{{"http", 0, 80, ""}}, 952 } 953 offer, err := idx.AssignTaskNetwork(ask) 954 if err != nil { 955 t.Fatalf("err: %v", err) 956 } 957 if offer == nil { 958 t.Fatalf("bad") 959 } 960 if offer.IP != "192.168.0.100" { 961 t.Fatalf("bad: %#v", offer) 962 } 963 if len(offer.DynamicPorts) != 1 { 964 t.Fatalf("There should be three dynamic ports") 965 } 966 if p := offer.DynamicPorts[0].Value; p != idx.MaxDynamicPort { 967 t.Fatalf("Dynamic Port: should have been assigned %d; got %d", p, idx.MaxDynamicPort) 968 } 969 } 970 971 func TestIntContains(t *testing.T) { 972 ci.Parallel(t) 973 974 l := []int{1, 2, 10, 20} 975 if isPortReserved(l, 50) { 976 t.Fatalf("bad") 977 } 978 if !isPortReserved(l, 20) { 979 t.Fatalf("bad") 980 } 981 if !isPortReserved(l, 1) { 982 t.Fatalf("bad") 983 } 984 } 985 986 func TestNetworkIndex_SetNode_HostNets(t *testing.T) { 987 ci.Parallel(t) 988 989 idx := NewNetworkIndex() 990 n := &Node{ 991 NodeResources: &NodeResources{ 992 Networks: []*NetworkResource{ 993 // As of Nomad v1.3 bridge networks get 994 // registered with only their mode set. 995 { 996 Mode: "bridge", 997 }, 998 999 // Localhost (agent interface) 1000 { 1001 CIDR: "127.0.0.1/32", 1002 Device: "lo", 1003 IP: "127.0.0.1", 1004 MBits: 1000, 1005 Mode: "host", 1006 }, 1007 { 1008 CIDR: "::1/128", 1009 Device: "lo", 1010 IP: "::1", 1011 MBits: 1000, 1012 Mode: "host", 1013 }, 1014 1015 // Node.NodeResources.Networks does *not* 1016 // contain host_networks. 1017 }, 1018 NodeNetworks: []*NodeNetworkResource{ 1019 // As of Nomad v1.3 bridge networks get 1020 // registered with only their mode set. 1021 { 1022 Mode: "bridge", 1023 }, 1024 { 1025 Addresses: []NodeNetworkAddress{ 1026 { 1027 Address: "127.0.0.1", 1028 Alias: "default", 1029 Family: "ipv4", 1030 }, 1031 { 1032 Address: "::1", 1033 Alias: "default", 1034 Family: "ipv6", 1035 }, 1036 }, 1037 Device: "lo", 1038 Mode: "host", 1039 Speed: 1000, 1040 }, 1041 { 1042 Addresses: []NodeNetworkAddress{ 1043 { 1044 Address: "192.168.0.1", 1045 Alias: "eth0", 1046 Family: "ipv4", 1047 ReservedPorts: "22", 1048 }, 1049 }, 1050 Device: "enxaaaaaaaaaaaa", 1051 MacAddress: "aa:aa:aa:aa:aa:aa", 1052 Mode: "host", 1053 Speed: 1000, 1054 }, 1055 { 1056 Addresses: []NodeNetworkAddress{ 1057 { 1058 Address: "192.168.1.1", 1059 Alias: "eth1", 1060 Family: "ipv4", 1061 ReservedPorts: "80", 1062 }, 1063 }, 1064 Device: "enxbbbbbbbbbbbb", 1065 MacAddress: "bb:bb:bb:bb:bb:bb", 1066 Mode: "host", 1067 Speed: 1000, 1068 }, 1069 }, 1070 }, 1071 ReservedResources: &NodeReservedResources{ 1072 Networks: NodeReservedNetworkResources{ 1073 ReservedHostPorts: "22", 1074 }, 1075 }, 1076 } 1077 1078 require.NoError(t, idx.SetNode(n)) 1079 1080 // TaskNetworks should only contain the bridge and agent network 1081 require.Len(t, idx.TaskNetworks, 2) 1082 1083 // Ports should be used across all 4 IPs 1084 require.Equal(t, 4, len(idx.UsedPorts)) 1085 1086 // 22 should be reserved on all IPs 1087 require.True(t, idx.UsedPorts["127.0.0.1"].Check(22)) 1088 require.True(t, idx.UsedPorts["::1"].Check(22)) 1089 require.True(t, idx.UsedPorts["192.168.0.1"].Check(22)) 1090 require.True(t, idx.UsedPorts["192.168.1.1"].Check(22)) 1091 1092 // 80 should only be reserved on eth1's address 1093 require.False(t, idx.UsedPorts["127.0.0.1"].Check(80)) 1094 require.False(t, idx.UsedPorts["::1"].Check(80)) 1095 require.False(t, idx.UsedPorts["192.168.0.1"].Check(80)) 1096 require.True(t, idx.UsedPorts["192.168.1.1"].Check(80)) 1097 }