Skip to content

Commit 167c81a

Browse files
authored
Merge pull request #3546 from hashicorp/f-heuristic
Better interface selection heuristic
2 parents 0cdc12b + 6a434bc commit 167c81a

File tree

4 files changed

+27
-116
lines changed

4 files changed

+27
-116
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ IMPROVEMENTS:
1717
* cli: Allocation create and modify times are displayed in a human readable
1818
relative format like `6 h ago` [GH-3449]
1919
* client: Added metrics to track state transitions of allocations [GH-3061]
20+
* client: When `network_interface` is unspecified use interface attached to
21+
default route [GH-3546]
2022
* driver/docker: Detect OOM kill event [GH-3459]
2123
* driver/docker: Adds support for adding host device to container via `--device` [GH-2938]
2224
* driver/qemu: Support graceful shutdowns on unix platforms [GH-3411]

client/fingerprint/network.go

+17-38
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log"
66
"net"
77

8+
sockaddr "github.com/hashicorp/go-sockaddr"
89
"github.com/hashicorp/nomad/client/config"
910
"github.com/hashicorp/nomad/nomad/structs"
1011
)
@@ -167,47 +168,25 @@ func (f *NetworkFingerprint) createNetworkResources(throughput int, intf *net.In
167168
return nwResources, nil
168169
}
169170

170-
// Checks if the device is marked UP by the operator
171-
func (f *NetworkFingerprint) isDeviceEnabled(intf *net.Interface) bool {
172-
return intf.Flags&net.FlagUp != 0
173-
}
174-
175-
// Checks if the device has any IP address configured
176-
func (f *NetworkFingerprint) deviceHasIpAddress(intf *net.Interface) bool {
177-
addrs, err := f.interfaceDetector.Addrs(intf)
178-
return err == nil && len(addrs) != 0
179-
}
180-
181-
func (n *NetworkFingerprint) isDeviceLoopBackOrPointToPoint(intf *net.Interface) bool {
182-
return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
183-
}
184-
185-
// Returns the interface with the name passed by user
186-
// If the name is blank then it iterates through all the devices
187-
// and finds one which is routable and marked as UP
188-
// It excludes PPP and lo devices unless they are specifically asked
171+
// Returns the interface with the name passed by user. If the name is blank, we
172+
// use the interface attached to the default route.
189173
func (f *NetworkFingerprint) findInterface(deviceName string) (*net.Interface, error) {
190-
var interfaces []net.Interface
191-
var err error
192-
193-
if deviceName != "" {
194-
return f.interfaceDetector.InterfaceByName(deviceName)
195-
}
196-
197-
var intfs []net.Interface
198-
199-
if intfs, err = f.interfaceDetector.Interfaces(); err != nil {
200-
return nil, err
201-
}
174+
// If we aren't given a device, look it up by using the interface with the default route
175+
if deviceName == "" {
176+
ri, err := sockaddr.NewRouteInfo()
177+
if err != nil {
178+
return nil, err
179+
}
202180

203-
for _, intf := range intfs {
204-
if f.isDeviceEnabled(&intf) && !f.isDeviceLoopBackOrPointToPoint(&intf) && f.deviceHasIpAddress(&intf) {
205-
interfaces = append(interfaces, intf)
181+
defaultIfName, err := ri.GetDefaultInterfaceName()
182+
if err != nil {
183+
return nil, err
206184
}
185+
if defaultIfName == "" {
186+
return nil, fmt.Errorf("no network_interface given and failed to determine interface attached to default route")
187+
}
188+
deviceName = defaultIfName
207189
}
208190

209-
if len(interfaces) == 0 {
210-
return nil, nil
211-
}
212-
return &interfaces[0], nil
191+
return f.interfaceDetector.InterfaceByName(deviceName)
213192
}

client/fingerprint/network_test.go

+2-73
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ type NetworkInterfaceDetectorMultipleInterfaces struct {
113113
}
114114

115115
func (n *NetworkInterfaceDetectorMultipleInterfaces) Interfaces() ([]net.Interface, error) {
116-
return []net.Interface{lo, eth0, eth1, eth2}, nil
116+
// Return link local first to test we don't prefer it
117+
return []net.Interface{lo, eth0, eth1, eth2, eth3, eth4}, nil
117118
}
118119

119120
func (n *NetworkInterfaceDetectorMultipleInterfaces) InterfaceByName(name string) (*net.Interface, error) {
@@ -224,23 +225,6 @@ func TestNetworkFingerprint_basic(t *testing.T) {
224225
}
225226
}
226227

227-
func TestNetworkFingerprint_no_devices(t *testing.T) {
228-
f := &NetworkFingerprint{logger: testLogger(), interfaceDetector: &NetworkIntefaceDetectorNoDevices{}}
229-
node := &structs.Node{
230-
Attributes: make(map[string]string),
231-
}
232-
cfg := &config.Config{NetworkSpeed: 100}
233-
234-
ok, err := f.Fingerprint(cfg, node)
235-
if err != nil {
236-
t.Fatalf("err: %v", err)
237-
}
238-
239-
if ok {
240-
t.Fatalf("ok: %v", ok)
241-
}
242-
}
243-
244228
func TestNetworkFingerprint_default_device_absent(t *testing.T) {
245229
f := &NetworkFingerprint{logger: testLogger(), interfaceDetector: &NetworkInterfaceDetectorOnlyLo{}}
246230
node := &structs.Node{
@@ -301,61 +285,6 @@ func TestNetworkFingerPrint_default_device(t *testing.T) {
301285
}
302286
}
303287

304-
func TestNetworkFingerPrint_excludelo_down_interfaces(t *testing.T) {
305-
f := &NetworkFingerprint{logger: testLogger(), interfaceDetector: &NetworkInterfaceDetectorMultipleInterfaces{}}
306-
node := &structs.Node{
307-
Attributes: make(map[string]string),
308-
}
309-
cfg := &config.Config{NetworkSpeed: 100}
310-
311-
ok, err := f.Fingerprint(cfg, node)
312-
if err != nil {
313-
t.Fatalf("err: %v", err)
314-
}
315-
if !ok {
316-
t.Fatalf("should apply")
317-
}
318-
319-
assertNodeAttributeContains(t, node, "unique.network.ip-address")
320-
321-
ip := node.Attributes["unique.network.ip-address"]
322-
match := net.ParseIP(ip)
323-
if match == nil {
324-
t.Fatalf("Bad IP match: %s", ip)
325-
}
326-
327-
if node.Resources == nil || len(node.Resources.Networks) == 0 {
328-
t.Fatal("Expected to find Network Resources")
329-
}
330-
331-
// Test at least the first Network Resource
332-
net := node.Resources.Networks[0]
333-
if net.IP == "" {
334-
t.Fatal("Expected Network Resource to have an IP")
335-
}
336-
if net.CIDR == "" {
337-
t.Fatal("Expected Network Resource to have a CIDR")
338-
}
339-
if net.Device != "eth0" {
340-
t.Fatal("Expected Network Resource to be eth0. Actual: ", net.Device)
341-
}
342-
if net.MBits == 0 {
343-
t.Fatal("Expected Network Resource to have a non-zero bandwidth")
344-
}
345-
346-
// Test the CIDR of the IPs
347-
if node.Resources.Networks[0].CIDR != "100.64.0.0/32" {
348-
t.Fatalf("bad CIDR: %v", node.Resources.Networks[0].CIDR)
349-
}
350-
if node.Resources.Networks[1].CIDR != "2001:db8:85a3::/128" {
351-
t.Fatalf("bad CIDR: %v", node.Resources.Networks[1].CIDR)
352-
}
353-
// Ensure that the link local address isn't fingerprinted
354-
if len(node.Resources.Networks) != 2 {
355-
t.Fatalf("bad number of IPs %v", len(node.Resources.Networks))
356-
}
357-
}
358-
359288
func TestNetworkFingerPrint_LinkLocal_Allowed(t *testing.T) {
360289
f := &NetworkFingerprint{logger: testLogger(), interfaceDetector: &NetworkInterfaceDetectorMultipleInterfaces{}}
361290
node := &structs.Node{

website/source/docs/agent/configuration/client.html.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,12 @@ client {
4949
- `meta` `(map[string]string: nil)` - Specifies a key-value map that annotates
5050
with user-defined metadata.
5151

52-
- `network_interface` `(string: "lo | lo0")` - Specifies the name of the
53-
interface to force network fingerprinting on. This defaults to the loopback
54-
interface. All addresses on the interface are fingerprinted except the ones
55-
which are scoped local for IPv6. When allocating ports for tasks, the
56-
scheduler will choose from the IPs of the fingerprinted interface.
52+
- `network_interface` `(string: varied)` - Specifies the name of the interface
53+
to force network fingerprinting on. When run in dev mode, this defaults to the
54+
loopback interface. When not in dev mode, the interface attached to the
55+
default route is used. All IP addresses except those scoped local for IPV6 on
56+
the chosen interface are fingerprinted. The scheduler chooses from those IP
57+
addresses when allocating ports for tasks.
5758

5859
- `network_speed` `(int: 0)` - Specifies an override for the network link speed.
5960
This value, if set, overrides any detected or defaulted link speed. Most

0 commit comments

Comments
 (0)