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 = ð0 129 case "eth1": 130 intf = ð1 131 case "eth2": 132 intf = ð2 133 case "eth3": 134 intf = ð3 135 case "eth4": 136 intf = ð4 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 }