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