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