github.com/smithx10/nomad@v0.9.1-rc1/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 = ð0 128 case "eth1": 129 intf = ð1 130 case "eth2": 131 intf = ð2 132 case "eth3": 133 intf = ð3 134 case "eth4": 135 intf = ð4 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 }