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