github.com/bigcommerce/nomad@v0.9.3-bc/client/fingerprint/network_test.go (about)

     1  package fingerprint
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/hashicorp/nomad/client/config"
    10  	"github.com/hashicorp/nomad/helper/testlog"
    11  	"github.com/hashicorp/nomad/nomad/structs"
    12  )
    13  
    14  // Set skipOnlineTestEnvVar to a non-empty value to skip network tests.  Useful
    15  // when working offline (e.g. an airplane).
    16  const skipOnlineTestsEnvVar = "TEST_NOMAD_SKIP_ONLINE_NET"
    17  
    18  var (
    19  	lo = net.Interface{
    20  		Index:        2,
    21  		MTU:          65536,
    22  		Name:         "lo",
    23  		HardwareAddr: []byte{23, 43, 54, 54},
    24  		Flags:        net.FlagUp | net.FlagLoopback,
    25  	}
    26  
    27  	eth0 = net.Interface{
    28  		Index:        3,
    29  		MTU:          1500,
    30  		Name:         "eth0",
    31  		HardwareAddr: []byte{23, 44, 54, 67},
    32  		Flags:        net.FlagUp | net.FlagMulticast | net.FlagBroadcast,
    33  	}
    34  
    35  	eth1 = net.Interface{
    36  		Index:        4,
    37  		MTU:          1500,
    38  		Name:         "eth1",
    39  		HardwareAddr: []byte{23, 44, 54, 69},
    40  		Flags:        net.FlagMulticast | net.FlagBroadcast,
    41  	}
    42  
    43  	eth2 = net.Interface{
    44  		Index:        4,
    45  		MTU:          1500,
    46  		Name:         "eth2",
    47  		HardwareAddr: []byte{23, 44, 54, 70},
    48  		Flags:        net.FlagUp | net.FlagBroadcast | net.FlagMulticast,
    49  	}
    50  
    51  	// One link local address
    52  	eth3 = net.Interface{
    53  		Index:        4,
    54  		MTU:          1500,
    55  		Name:         "eth3",
    56  		HardwareAddr: []byte{23, 44, 54, 71},
    57  		Flags:        net.FlagUp | net.FlagBroadcast | net.FlagMulticast,
    58  	}
    59  
    60  	// One link local address and one globally routable address
    61  	eth4 = net.Interface{
    62  		Index:        4,
    63  		MTU:          1500,
    64  		Name:         "eth4",
    65  		HardwareAddr: []byte{23, 44, 54, 72},
    66  		Flags:        net.FlagUp | net.FlagBroadcast | net.FlagMulticast,
    67  	}
    68  )
    69  
    70  // A fake network detector which returns no devices
    71  type NetworkInterfaceDetectorNoDevices struct {
    72  }
    73  
    74  func (f *NetworkInterfaceDetectorNoDevices) Interfaces() ([]net.Interface, error) {
    75  	return make([]net.Interface, 0), nil
    76  }
    77  
    78  func (f *NetworkInterfaceDetectorNoDevices) InterfaceByName(name string) (*net.Interface, error) {
    79  	return nil, fmt.Errorf("Device with name %s doesn't exist", name)
    80  }
    81  
    82  func (f *NetworkInterfaceDetectorNoDevices) Addrs(intf *net.Interface) ([]net.Addr, error) {
    83  	return nil, fmt.Errorf("No interfaces found for device %v", intf.Name)
    84  }
    85  
    86  // A fake network detector which returns only loopback
    87  type NetworkInterfaceDetectorOnlyLo struct {
    88  }
    89  
    90  func (n *NetworkInterfaceDetectorOnlyLo) Interfaces() ([]net.Interface, error) {
    91  	return []net.Interface{lo}, nil
    92  }
    93  
    94  func (n *NetworkInterfaceDetectorOnlyLo) InterfaceByName(name string) (*net.Interface, error) {
    95  	if name == "lo" {
    96  		return &lo, nil
    97  	}
    98  
    99  	return nil, fmt.Errorf("No device with name %v found", name)
   100  }
   101  
   102  func (n *NetworkInterfaceDetectorOnlyLo) Addrs(intf *net.Interface) ([]net.Addr, error) {
   103  	if intf.Name == "lo" {
   104  		_, ipnet1, _ := net.ParseCIDR("127.0.0.1/8")
   105  		_, ipnet2, _ := net.ParseCIDR("2001:DB8::/48")
   106  		return []net.Addr{ipnet1, ipnet2}, nil
   107  	}
   108  
   109  	return nil, fmt.Errorf("Can't find addresses for device: %v", intf.Name)
   110  }
   111  
   112  // A fake network detector which simulates the presence of multiple interfaces
   113  type NetworkInterfaceDetectorMultipleInterfaces struct {
   114  }
   115  
   116  func (n *NetworkInterfaceDetectorMultipleInterfaces) Interfaces() ([]net.Interface, error) {
   117  	// Return link local first to test we don't prefer it
   118  	return []net.Interface{lo, eth0, eth1, eth2, eth3, eth4}, nil
   119  }
   120  
   121  func (n *NetworkInterfaceDetectorMultipleInterfaces) InterfaceByName(name string) (*net.Interface, error) {
   122  	var intf *net.Interface
   123  	switch name {
   124  	case "lo":
   125  		intf = &lo
   126  	case "eth0":
   127  		intf = &eth0
   128  	case "eth1":
   129  		intf = &eth1
   130  	case "eth2":
   131  		intf = &eth2
   132  	case "eth3":
   133  		intf = &eth3
   134  	case "eth4":
   135  		intf = &eth4
   136  	}
   137  	if intf != nil {
   138  		return intf, nil
   139  	}
   140  
   141  	return nil, fmt.Errorf("No device with name %v found", name)
   142  }
   143  
   144  func (n *NetworkInterfaceDetectorMultipleInterfaces) Addrs(intf *net.Interface) ([]net.Addr, error) {
   145  	if intf.Name == "lo" {
   146  		_, ipnet1, _ := net.ParseCIDR("127.0.0.1/8")
   147  		_, ipnet2, _ := net.ParseCIDR("2001:DB8::/48")
   148  		return []net.Addr{ipnet1, ipnet2}, nil
   149  	}
   150  
   151  	if intf.Name == "eth0" {
   152  		_, ipnet1, _ := net.ParseCIDR("100.64.0.11/10")
   153  		_, ipnet2, _ := net.ParseCIDR("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64")
   154  		ipAddr, _ := net.ResolveIPAddr("ip6", "fe80::140c:9579:8037:f565")
   155  		return []net.Addr{ipnet1, ipnet2, ipAddr}, nil
   156  	}
   157  
   158  	if intf.Name == "eth1" {
   159  		_, ipnet1, _ := net.ParseCIDR("100.64.0.10/10")
   160  		_, ipnet2, _ := net.ParseCIDR("2003:DB8::/48")
   161  		return []net.Addr{ipnet1, ipnet2}, nil
   162  	}
   163  
   164  	if intf.Name == "eth2" {
   165  		return []net.Addr{}, nil
   166  	}
   167  
   168  	if intf.Name == "eth3" {
   169  		_, ipnet1, _ := net.ParseCIDR("169.254.155.20/32")
   170  		return []net.Addr{ipnet1}, nil
   171  	}
   172  
   173  	if intf.Name == "eth4" {
   174  		_, ipnet1, _ := net.ParseCIDR("169.254.155.20/32")
   175  		_, ipnet2, _ := net.ParseCIDR("100.64.0.10/10")
   176  		return []net.Addr{ipnet1, ipnet2}, nil
   177  	}
   178  
   179  	return nil, fmt.Errorf("Can't find addresses for device: %v", intf.Name)
   180  }
   181  
   182  func TestNetworkFingerprint_basic(t *testing.T) {
   183  	if v := os.Getenv(skipOnlineTestsEnvVar); v != "" {
   184  		t.Skipf("Environment variable %+q not empty, skipping test", skipOnlineTestsEnvVar)
   185  	}
   186  
   187  	f := &NetworkFingerprint{logger: testlog.HCLogger(t), interfaceDetector: &DefaultNetworkInterfaceDetector{}}
   188  	node := &structs.Node{
   189  		Attributes: make(map[string]string),
   190  	}
   191  	cfg := &config.Config{NetworkSpeed: 101}
   192  
   193  	request := &FingerprintRequest{Config: cfg, Node: node}
   194  	var response FingerprintResponse
   195  	err := f.Fingerprint(request, &response)
   196  	if err != nil {
   197  		t.Fatalf("err: %v", err)
   198  	}
   199  
   200  	if !response.Detected {
   201  		t.Fatalf("expected response to be applicable")
   202  	}
   203  
   204  	attributes := response.Attributes
   205  	if len(attributes) == 0 {
   206  		t.Fatalf("should apply (HINT: working offline? Set env %q=y", skipOnlineTestsEnvVar)
   207  	}
   208  
   209  	assertNodeAttributeContains(t, attributes, "unique.network.ip-address")
   210  
   211  	ip := attributes["unique.network.ip-address"]
   212  	match := net.ParseIP(ip)
   213  	if match == nil {
   214  		t.Fatalf("Bad IP match: %s", ip)
   215  	}
   216  
   217  	if response.Resources == nil || len(response.Resources.Networks) == 0 {
   218  		t.Fatal("Expected to find Network Resources")
   219  	}
   220  
   221  	// Test at least the first Network Resource
   222  	net := response.Resources.Networks[0]
   223  	if net.IP == "" {
   224  		t.Fatal("Expected Network Resource to not be empty")
   225  	}
   226  	if net.CIDR == "" {
   227  		t.Fatal("Expected Network Resource to have a CIDR")
   228  	}
   229  	if net.Device == "" {
   230  		t.Fatal("Expected Network Resource to have a Device Name")
   231  	}
   232  	if net.MBits != 101 {
   233  		t.Fatalf("Expected Network Resource to have bandwidth %d; got %d", 101, net.MBits)
   234  	}
   235  }
   236  
   237  func TestNetworkFingerprint_default_device_absent(t *testing.T) {
   238  	f := &NetworkFingerprint{logger: testlog.HCLogger(t), interfaceDetector: &NetworkInterfaceDetectorOnlyLo{}}
   239  	node := &structs.Node{
   240  		Attributes: make(map[string]string),
   241  	}
   242  	cfg := &config.Config{NetworkSpeed: 100, NetworkInterface: "eth0"}
   243  
   244  	request := &FingerprintRequest{Config: cfg, Node: node}
   245  	var response FingerprintResponse
   246  	err := f.Fingerprint(request, &response)
   247  	if err == nil {
   248  		t.Fatalf("err: %v", err)
   249  	}
   250  
   251  	if response.Detected {
   252  		t.Fatalf("expected response to not be applicable")
   253  	}
   254  
   255  	if len(response.Attributes) != 0 {
   256  		t.Fatalf("attributes should be zero but instead are: %v", response.Attributes)
   257  	}
   258  }
   259  
   260  func TestNetworkFingerPrint_default_device(t *testing.T) {
   261  	f := &NetworkFingerprint{logger: testlog.HCLogger(t), interfaceDetector: &NetworkInterfaceDetectorOnlyLo{}}
   262  	node := &structs.Node{
   263  		Attributes: make(map[string]string),
   264  	}
   265  	cfg := &config.Config{NetworkSpeed: 100, NetworkInterface: "lo"}
   266  
   267  	request := &FingerprintRequest{Config: cfg, Node: node}
   268  	var response FingerprintResponse
   269  	err := f.Fingerprint(request, &response)
   270  	if err != nil {
   271  		t.Fatalf("err: %v", err)
   272  	}
   273  
   274  	if !response.Detected {
   275  		t.Fatalf("expected response to be applicable")
   276  	}
   277  
   278  	attributes := response.Attributes
   279  	if len(attributes) == 0 {
   280  		t.Fatalf("should apply")
   281  	}
   282  
   283  	assertNodeAttributeContains(t, attributes, "unique.network.ip-address")
   284  
   285  	ip := attributes["unique.network.ip-address"]
   286  	match := net.ParseIP(ip)
   287  	if match == nil {
   288  		t.Fatalf("Bad IP match: %s", ip)
   289  	}
   290  
   291  	if response.Resources == nil || len(response.Resources.Networks) == 0 {
   292  		t.Fatal("Expected to find Network Resources")
   293  	}
   294  
   295  	// Test at least the first Network Resource
   296  	net := response.Resources.Networks[0]
   297  	if net.IP == "" {
   298  		t.Fatal("Expected Network Resource to not be empty")
   299  	}
   300  	if net.CIDR == "" {
   301  		t.Fatal("Expected Network Resource to have a CIDR")
   302  	}
   303  	if net.Device == "" {
   304  		t.Fatal("Expected Network Resource to have a Device Name")
   305  	}
   306  	if net.MBits == 0 {
   307  		t.Fatal("Expected Network Resource to have a non-zero bandwidth")
   308  	}
   309  }
   310  
   311  func TestNetworkFingerPrint_LinkLocal_Allowed(t *testing.T) {
   312  	f := &NetworkFingerprint{logger: testlog.HCLogger(t), interfaceDetector: &NetworkInterfaceDetectorMultipleInterfaces{}}
   313  	node := &structs.Node{
   314  		Attributes: make(map[string]string),
   315  	}
   316  	cfg := &config.Config{NetworkSpeed: 100, NetworkInterface: "eth3"}
   317  
   318  	request := &FingerprintRequest{Config: cfg, Node: node}
   319  	var response FingerprintResponse
   320  	err := f.Fingerprint(request, &response)
   321  	if err != nil {
   322  		t.Fatalf("err: %v", err)
   323  	}
   324  
   325  	if !response.Detected {
   326  		t.Fatalf("expected response to be applicable")
   327  	}
   328  
   329  	attributes := response.Attributes
   330  	assertNodeAttributeContains(t, attributes, "unique.network.ip-address")
   331  
   332  	ip := attributes["unique.network.ip-address"]
   333  	match := net.ParseIP(ip)
   334  	if match == nil {
   335  		t.Fatalf("Bad IP match: %s", ip)
   336  	}
   337  
   338  	if response.Resources == nil || len(response.Resources.Networks) == 0 {
   339  		t.Fatal("Expected to find Network Resources")
   340  	}
   341  
   342  	// Test at least the first Network Resource
   343  	net := response.Resources.Networks[0]
   344  	if net.IP == "" {
   345  		t.Fatal("Expected Network Resource to not be empty")
   346  	}
   347  	if net.CIDR == "" {
   348  		t.Fatal("Expected Network Resource to have a CIDR")
   349  	}
   350  	if net.Device == "" {
   351  		t.Fatal("Expected Network Resource to have a Device Name")
   352  	}
   353  	if net.MBits == 0 {
   354  		t.Fatal("Expected Network Resource to have a non-zero bandwidth")
   355  	}
   356  }
   357  
   358  func TestNetworkFingerPrint_LinkLocal_Allowed_MixedIntf(t *testing.T) {
   359  	f := &NetworkFingerprint{logger: testlog.HCLogger(t), interfaceDetector: &NetworkInterfaceDetectorMultipleInterfaces{}}
   360  	node := &structs.Node{
   361  		Attributes: make(map[string]string),
   362  	}
   363  	cfg := &config.Config{NetworkSpeed: 100, NetworkInterface: "eth4"}
   364  
   365  	request := &FingerprintRequest{Config: cfg, Node: node}
   366  	var response FingerprintResponse
   367  	err := f.Fingerprint(request, &response)
   368  	if err != nil {
   369  		t.Fatalf("err: %v", err)
   370  	}
   371  
   372  	if !response.Detected {
   373  		t.Fatalf("expected response to be applicable")
   374  	}
   375  
   376  	attributes := response.Attributes
   377  	if len(attributes) == 0 {
   378  		t.Fatalf("should apply attributes")
   379  	}
   380  
   381  	assertNodeAttributeContains(t, attributes, "unique.network.ip-address")
   382  
   383  	ip := attributes["unique.network.ip-address"]
   384  	match := net.ParseIP(ip)
   385  	if match == nil {
   386  		t.Fatalf("Bad IP match: %s", ip)
   387  	}
   388  
   389  	if response.Resources == nil || len(response.Resources.Networks) == 0 {
   390  		t.Fatal("Expected to find Network Resources")
   391  	}
   392  
   393  	// Test at least the first Network Resource
   394  	net := response.Resources.Networks[0]
   395  	if net.IP == "" {
   396  		t.Fatal("Expected Network Resource to not be empty")
   397  	}
   398  	if net.IP == "169.254.155.20" {
   399  		t.Fatalf("expected non-link local address; got %v", net.IP)
   400  	}
   401  	if net.CIDR == "" {
   402  		t.Fatal("Expected Network Resource to have a CIDR")
   403  	}
   404  	if net.Device == "" {
   405  		t.Fatal("Expected Network Resource to have a Device Name")
   406  	}
   407  	if net.MBits == 0 {
   408  		t.Fatal("Expected Network Resource to have a non-zero bandwidth")
   409  	}
   410  }
   411  
   412  func TestNetworkFingerPrint_LinkLocal_Disallowed(t *testing.T) {
   413  	f := &NetworkFingerprint{logger: testlog.HCLogger(t), interfaceDetector: &NetworkInterfaceDetectorMultipleInterfaces{}}
   414  	node := &structs.Node{
   415  		Attributes: make(map[string]string),
   416  	}
   417  	cfg := &config.Config{
   418  		NetworkSpeed:     100,
   419  		NetworkInterface: "eth3",
   420  		Options: map[string]string{
   421  			networkDisallowLinkLocalOption: "true",
   422  		},
   423  	}
   424  
   425  	request := &FingerprintRequest{Config: cfg, Node: node}
   426  	var response FingerprintResponse
   427  	err := f.Fingerprint(request, &response)
   428  	if err != nil {
   429  		t.Fatalf("err: %v", err)
   430  	}
   431  
   432  	if !response.Detected {
   433  		t.Fatalf("expected response to be applicable")
   434  	}
   435  
   436  	if len(response.Attributes) != 0 {
   437  		t.Fatalf("should not apply attributes")
   438  	}
   439  }