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  }