From fde09a21d0172f6f9c7ce1cf7c804e587deefc81 Mon Sep 17 00:00:00 2001 From: aldernero Date: Mon, 31 Jan 2022 20:50:40 -0700 Subject: [PATCH 1/5] autodetect interfaces on private networks --- netutil/netutil.go | 50 ++++++++++++++++ netutil/netutil_test.go | 126 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 netutil/netutil.go create mode 100644 netutil/netutil_test.go diff --git a/netutil/netutil.go b/netutil/netutil.go new file mode 100644 index 000000000..e8f2aaf5b --- /dev/null +++ b/netutil/netutil.go @@ -0,0 +1,50 @@ +package netutil + +import ( + "net" + "strings" + + "github.com/go-kit/log" + "github.com/go-kit/log/level" +) + +var ( + getInterfaceAddrs = (*net.Interface).Addrs +) + +// Parses network interfaces and returns those having an address conformant to RFC1918 +func PrivateNetworkInterfaces(logger log.Logger) []string { + ints, err := net.Interfaces() + if err != nil { + level.Warn(logger).Log("msg", "error getting interfaces", "err", err) + } + return privateNetworkInterfaces(ints, logger) +} + +// private testable function that checks each given interface +func privateNetworkInterfaces(all []net.Interface, logger log.Logger) []string { + var privInts []string + for _, i := range all { + addrs, err := getInterfaceAddrs(&i) + if err != nil { + level.Warn(logger).Log("msg", "error getting addresses", "inf", i.Name, "err", err) + } + for _, a := range addrs { + s := a.String() + ip, _, err := net.ParseCIDR(s) + if err != nil { + level.Warn(logger).Log("msg", "error getting ip address", "inf", i.Name, "addr", s, "err", err) + } + if ip.IsPrivate() { + level.Info(logger).Log(i.Name, "is private with address", s) + privInts = append(privInts, i.Name) + break + } + } + } + if len(privInts) == 0 { + return []string{"eth0", "en0"} + } + level.Info(logger).Log("msg", "found interfaces on private networks:", "["+strings.Join(privInts, ", ")+"]") + return privInts +} diff --git a/netutil/netutil_test.go b/netutil/netutil_test.go new file mode 100644 index 000000000..7d835b744 --- /dev/null +++ b/netutil/netutil_test.go @@ -0,0 +1,126 @@ +package netutil + +import ( + "net" + "os" + "reflect" + "testing" + + "github.com/go-kit/log" +) + +// Setup required logger and example interface names and addresses +var ( + logger log.Logger = log.NewLogfmtLogger(os.Stdout) + testIntsAddrs = map[string]string{ + "privNetA": "10.6.19.34/8", + "privNetB": "172.16.0.7/12", + "privNetC": "192.168.3.29/24", + "pubNet": "34.120.177.193/24", + } +) + +// A type that implements the net.Addr interface +// Only String() is called by netutil logic +type MockAddr struct { + netAddr string +} + +func (ma MockAddr) Network() string { + return "tcp" +} + +func (ma MockAddr) String() string { + return ma.netAddr +} + +// Helper function to test a list of interfaces +func generateTestInterfaces(names []string) []net.Interface { + testInts := []net.Interface{} + for i, j := range names { + k := net.Interface{ + Index: i + 1, + MTU: 1500, + Name: j, + HardwareAddr: []byte{}, + Flags: 0, + } + testInts = append(testInts, k) + } + return testInts +} + +func TestEmptyInterface(t *testing.T) { + ints := []net.Interface{} + getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { + return []net.Addr{}, nil + } + privInts := privateNetworkInterfaces(ints, logger) + if !reflect.DeepEqual(privInts, []string{"eth0", "en0"}) { + t.Errorf("Expected default/fallback interfaces, got %v\n", privInts) + } +} + +func TestSinglePrivateInterface(t *testing.T) { + ifname := "privNetA" + ints := []net.Interface{{ + Index: 1, + MTU: 1500, + Name: ifname, + HardwareAddr: []byte{}, + Flags: 0, + }} + getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { + return []net.Addr{MockAddr{netAddr: testIntsAddrs[ifname]}}, nil + } + privInts := privateNetworkInterfaces(ints, logger) + if !reflect.DeepEqual(privInts, []string{ifname}) { + t.Errorf("Expected single result {\"%s\"}, got %v\n", ifname, privInts) + } +} + +func TestSinglePublicInterface(t *testing.T) { + ifname := "pubNet" + ints := []net.Interface{{ + Index: 1, + MTU: 1500, + Name: ifname, + HardwareAddr: []byte{}, + Flags: 0, + }} + getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { + return []net.Addr{MockAddr{netAddr: testIntsAddrs[ifname]}}, nil + } + privInts := privateNetworkInterfaces(ints, logger) + if !reflect.DeepEqual(privInts, []string{"eth0", "en0"}) { + t.Errorf("Expected default/fallback interfaces, got %v\n", privInts) + } +} + +func TestListAllPrivate(t *testing.T) { + intNames := []string{"privNetA", "privNetB", "privNetC"} + ints := generateTestInterfaces(intNames) + getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { + return []net.Addr{ + MockAddr{netAddr: testIntsAddrs[i.Name]}, + }, nil + } + privInts := privateNetworkInterfaces(ints, logger) + if !reflect.DeepEqual(privInts, intNames) { + t.Errorf("Expected all input interfaces, got %v\n", privInts) + } +} + +func TestMixPrivatePublic(t *testing.T) { + intNames := []string{"pubNet", "privNetA", "privNetB", "privNetC"} + ints := generateTestInterfaces(intNames) + getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { + return []net.Addr{ + MockAddr{netAddr: testIntsAddrs[i.Name]}, + }, nil + } + privInts := privateNetworkInterfaces(ints, logger) + if !reflect.DeepEqual(privInts, []string{"privNetA", "privNetB", "privNetC"}) { + t.Errorf("Expected all private interfaces, got %v\n", privInts) + } +} From 9dfc4e73d6594dca284d1adc0deaf09ab38b0c79 Mon Sep 17 00:00:00 2001 From: aldernero Date: Tue, 1 Feb 2022 12:46:02 -0700 Subject: [PATCH 2/5] removed debug code, improved tests --- netutil/netutil.go | 1 - netutil/netutil_test.go | 22 ++++++---------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/netutil/netutil.go b/netutil/netutil.go index e8f2aaf5b..09b63703a 100644 --- a/netutil/netutil.go +++ b/netutil/netutil.go @@ -36,7 +36,6 @@ func privateNetworkInterfaces(all []net.Interface, logger log.Logger) []string { level.Warn(logger).Log("msg", "error getting ip address", "inf", i.Name, "addr", s, "err", err) } if ip.IsPrivate() { - level.Info(logger).Log(i.Name, "is private with address", s) privInts = append(privInts, i.Name) break } diff --git a/netutil/netutil_test.go b/netutil/netutil_test.go index 7d835b744..1aaab2ab8 100644 --- a/netutil/netutil_test.go +++ b/netutil/netutil_test.go @@ -3,10 +3,10 @@ package netutil import ( "net" "os" - "reflect" "testing" "github.com/go-kit/log" + "github.com/stretchr/testify/assert" ) // Setup required logger and example interface names and addresses @@ -56,9 +56,7 @@ func TestEmptyInterface(t *testing.T) { return []net.Addr{}, nil } privInts := privateNetworkInterfaces(ints, logger) - if !reflect.DeepEqual(privInts, []string{"eth0", "en0"}) { - t.Errorf("Expected default/fallback interfaces, got %v\n", privInts) - } + assert.Equal(t, privInts, []string{"eth0", "en0"}) } func TestSinglePrivateInterface(t *testing.T) { @@ -74,9 +72,7 @@ func TestSinglePrivateInterface(t *testing.T) { return []net.Addr{MockAddr{netAddr: testIntsAddrs[ifname]}}, nil } privInts := privateNetworkInterfaces(ints, logger) - if !reflect.DeepEqual(privInts, []string{ifname}) { - t.Errorf("Expected single result {\"%s\"}, got %v\n", ifname, privInts) - } + assert.Equal(t, privInts, []string{ifname}) } func TestSinglePublicInterface(t *testing.T) { @@ -92,9 +88,7 @@ func TestSinglePublicInterface(t *testing.T) { return []net.Addr{MockAddr{netAddr: testIntsAddrs[ifname]}}, nil } privInts := privateNetworkInterfaces(ints, logger) - if !reflect.DeepEqual(privInts, []string{"eth0", "en0"}) { - t.Errorf("Expected default/fallback interfaces, got %v\n", privInts) - } + assert.Equal(t, privInts, []string{"eth0", "en0"}) } func TestListAllPrivate(t *testing.T) { @@ -106,9 +100,7 @@ func TestListAllPrivate(t *testing.T) { }, nil } privInts := privateNetworkInterfaces(ints, logger) - if !reflect.DeepEqual(privInts, intNames) { - t.Errorf("Expected all input interfaces, got %v\n", privInts) - } + assert.Equal(t, privInts, intNames) } func TestMixPrivatePublic(t *testing.T) { @@ -120,7 +112,5 @@ func TestMixPrivatePublic(t *testing.T) { }, nil } privInts := privateNetworkInterfaces(ints, logger) - if !reflect.DeepEqual(privInts, []string{"privNetA", "privNetB", "privNetC"}) { - t.Errorf("Expected all private interfaces, got %v\n", privInts) - } + assert.Equal(t, privInts, []string{"privNetA", "privNetB", "privNetC"}) } From a9283b44537f0434b58b8cfcb317ff333667e287 Mon Sep 17 00:00:00 2001 From: aldernero Date: Wed, 9 Feb 2022 15:53:47 -0700 Subject: [PATCH 3/5] cleaned up and improved unit tests --- netutil/netutil.go | 11 ++- netutil/netutil_test.go | 150 +++++++++++++++++++++------------------- 2 files changed, 85 insertions(+), 76 deletions(-) diff --git a/netutil/netutil.go b/netutil/netutil.go index 09b63703a..d06065e89 100644 --- a/netutil/netutil.go +++ b/netutil/netutil.go @@ -2,7 +2,6 @@ package netutil import ( "net" - "strings" "github.com/go-kit/log" "github.com/go-kit/log/level" @@ -12,11 +11,11 @@ var ( getInterfaceAddrs = (*net.Interface).Addrs ) -// Parses network interfaces and returns those having an address conformant to RFC1918 +// PrivateNetworkInterfaces lists network interfaces and returns those having an address conformant to RFC1918 func PrivateNetworkInterfaces(logger log.Logger) []string { ints, err := net.Interfaces() if err != nil { - level.Warn(logger).Log("msg", "error getting interfaces", "err", err) + level.Warn(logger).Log("msg", "error getting network interfaces", "err", err) } return privateNetworkInterfaces(ints, logger) } @@ -27,13 +26,13 @@ func privateNetworkInterfaces(all []net.Interface, logger log.Logger) []string { for _, i := range all { addrs, err := getInterfaceAddrs(&i) if err != nil { - level.Warn(logger).Log("msg", "error getting addresses", "inf", i.Name, "err", err) + level.Warn(logger).Log("msg", "error getting addresses from network interface", "interface", i.Name, "err", err) } for _, a := range addrs { s := a.String() ip, _, err := net.ParseCIDR(s) if err != nil { - level.Warn(logger).Log("msg", "error getting ip address", "inf", i.Name, "addr", s, "err", err) + level.Warn(logger).Log("msg", "error parsing network interface IP address", "inf", i.Name, "addr", s, "err", err) } if ip.IsPrivate() { privInts = append(privInts, i.Name) @@ -44,6 +43,6 @@ func privateNetworkInterfaces(all []net.Interface, logger log.Logger) []string { if len(privInts) == 0 { return []string{"eth0", "en0"} } - level.Info(logger).Log("msg", "found interfaces on private networks:", "["+strings.Join(privInts, ", ")+"]") + level.Debug(logger).Log("msg", "found network interfaces with private IP addresses assigned", "interfaces", privInts) return privInts } diff --git a/netutil/netutil_test.go b/netutil/netutil_test.go index 1aaab2ab8..725bc7b52 100644 --- a/netutil/netutil_test.go +++ b/netutil/netutil_test.go @@ -9,28 +9,17 @@ import ( "github.com/stretchr/testify/assert" ) -// Setup required logger and example interface names and addresses -var ( - logger log.Logger = log.NewLogfmtLogger(os.Stdout) - testIntsAddrs = map[string]string{ - "privNetA": "10.6.19.34/8", - "privNetB": "172.16.0.7/12", - "privNetC": "192.168.3.29/24", - "pubNet": "34.120.177.193/24", - } -) - // A type that implements the net.Addr interface // Only String() is called by netutil logic -type MockAddr struct { +type mockAddr struct { netAddr string } -func (ma MockAddr) Network() string { +func (ma mockAddr) Network() string { return "tcp" } -func (ma MockAddr) String() string { +func (ma mockAddr) String() string { return ma.netAddr } @@ -50,67 +39,88 @@ func generateTestInterfaces(names []string) []net.Interface { return testInts } -func TestEmptyInterface(t *testing.T) { - ints := []net.Interface{} - getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { - return []net.Addr{}, nil - } - privInts := privateNetworkInterfaces(ints, logger) - assert.Equal(t, privInts, []string{"eth0", "en0"}) -} - -func TestSinglePrivateInterface(t *testing.T) { - ifname := "privNetA" - ints := []net.Interface{{ - Index: 1, - MTU: 1500, - Name: ifname, - HardwareAddr: []byte{}, - Flags: 0, - }} - getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { - return []net.Addr{MockAddr{netAddr: testIntsAddrs[ifname]}}, nil +func TestPrivateInterface(t *testing.T) { + testIntsAddrs := map[string][]string{ + "privNetA": {"10.6.19.34/8"}, + "privNetB": {"172.16.0.7/12"}, + "privNetC": {"192.168.3.29/24"}, + "pubNet": {"34.120.177.193/24"}, + "multiPriv": {"10.6.19.34/8", "172.16.0.7/12"}, + "multiMix": {"1.1.1.1/24", "192.168.0.42/24"}, + "multiPub": {"1.1.1.1/24", "34.120.177.193/24"}, } - privInts := privateNetworkInterfaces(ints, logger) - assert.Equal(t, privInts, []string{ifname}) -} - -func TestSinglePublicInterface(t *testing.T) { - ifname := "pubNet" - ints := []net.Interface{{ - Index: 1, - MTU: 1500, - Name: ifname, - HardwareAddr: []byte{}, - Flags: 0, - }} - getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { - return []net.Addr{MockAddr{netAddr: testIntsAddrs[ifname]}}, nil + defaultOutput := []string{"eth0", "en0"} + type testCases struct { + description string + interfaces []string + expectedOutput []string } - privInts := privateNetworkInterfaces(ints, logger) - assert.Equal(t, privInts, []string{"eth0", "en0"}) -} - -func TestListAllPrivate(t *testing.T) { - intNames := []string{"privNetA", "privNetB", "privNetC"} - ints := generateTestInterfaces(intNames) - getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { - return []net.Addr{ - MockAddr{netAddr: testIntsAddrs[i.Name]}, - }, nil + for _, scenario := range []testCases{ + { + description: "empty interface list", + interfaces: []string{}, + expectedOutput: defaultOutput, + }, + { + description: "single private interface", + interfaces: []string{"privNetA"}, + expectedOutput: []string{"privNetA"}, + }, + { + description: "single public interface", + interfaces: []string{"pubNet"}, + expectedOutput: defaultOutput, + }, + { + description: "single interface multi address private", + interfaces: []string{"multiPriv"}, + expectedOutput: []string{"multiPriv"}, + }, + { + description: "single interface multi address mix", + interfaces: []string{"multiMix"}, + expectedOutput: []string{"multiMix"}, + }, + { + description: "single interface multi address public", + interfaces: []string{"multiPub"}, + expectedOutput: defaultOutput, + }, + { + description: "all private interfaces", + interfaces: []string{"privNetA", "privNetB", "privNetC"}, + expectedOutput: []string{"privNetA", "privNetB", "privNetC"}, + }, + { + description: "mix of public and private interfaces", + interfaces: []string{"pubNet", "privNetA", "privNetB", "privNetC", "multiPriv", "multiMix", "multiPub"}, + expectedOutput: []string{"privNetA", "privNetB", "privNetC", "multiPriv", "multiMix"}, + }, + } { + getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { + addrs := []net.Addr{} + for _, ip := range testIntsAddrs[i.Name] { + addrs = append(addrs, mockAddr{netAddr: ip}) + } + return addrs, nil + } + t.Run(scenario.description, func(t *testing.T) { + privInts := privateNetworkInterfaces( + generateTestInterfaces(scenario.interfaces), + log.NewNopLogger(), + ) + assert.Equal(t, privInts, scenario.expectedOutput) + }) } - privInts := privateNetworkInterfaces(ints, logger) - assert.Equal(t, privInts, intNames) } -func TestMixPrivatePublic(t *testing.T) { - intNames := []string{"pubNet", "privNetA", "privNetB", "privNetC"} - ints := generateTestInterfaces(intNames) +func TestPrivateInterfaceError(t *testing.T) { + interfaces := generateTestInterfaces([]string{"eth9"}) + ipaddr := "not_a_parseable_ip_string" getInterfaceAddrs = func(i *net.Interface) ([]net.Addr, error) { - return []net.Addr{ - MockAddr{netAddr: testIntsAddrs[i.Name]}, - }, nil + return []net.Addr{mockAddr{netAddr: ipaddr}}, nil } - privInts := privateNetworkInterfaces(ints, logger) - assert.Equal(t, privInts, []string{"privNetA", "privNetB", "privNetC"}) + logger := log.NewLogfmtLogger(os.Stdout) + privInts := privateNetworkInterfaces(interfaces, logger) + assert.Equal(t, privInts, []string{"eth0", "en0"}) } From 45071dbd0527d228ee84252d46a16958ba1294cf Mon Sep 17 00:00:00 2001 From: aldernero Date: Wed, 9 Feb 2022 16:43:07 -0700 Subject: [PATCH 4/5] lifecycler uses interface autodetect --- ring/lifecycler.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ring/lifecycler.go b/ring/lifecycler.go index 68c012238..195dfe2af 100644 --- a/ring/lifecycler.go +++ b/ring/lifecycler.go @@ -19,6 +19,7 @@ import ( "github.com/grafana/dskit/flagext" "github.com/grafana/dskit/kv" + "github.com/grafana/dskit/netutil" "github.com/grafana/dskit/services" ) @@ -81,7 +82,7 @@ func (cfg *LifecyclerConfig) RegisterFlagsWithPrefix(prefix string, f *flag.Flag panic(fmt.Errorf("failed to get hostname %s", err)) } - cfg.InfNames = []string{"eth0", "en0"} + cfg.InfNames = netutil.PrivateNetworkInterfaces(log.NewLogfmtLogger(os.Stdout)) f.Var((*flagext.StringSlice)(&cfg.InfNames), prefix+"lifecycler.interface", "Name of network interface to read address from.") f.StringVar(&cfg.Addr, prefix+"lifecycler.addr", "", "IP address to advertise in the ring.") f.IntVar(&cfg.Port, prefix+"lifecycler.port", 0, "port to advertise in consul (defaults to server.grpc-listen-port).") From 2188570bc8dbe771389157bdee61a04139eac8f0 Mon Sep 17 00:00:00 2001 From: aldernero Date: Thu, 10 Feb 2022 09:46:03 -0700 Subject: [PATCH 5/5] Merge branch 'add-netutil-package' of github.com:grafana/dskit into add-netutil-package --- netutil/netutil.go | 17 +++++++++++++---- netutil/netutil_test.go | 5 +++-- ring/lifecycler.go | 8 ++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/netutil/netutil.go b/netutil/netutil.go index d06065e89..a1b7c1d40 100644 --- a/netutil/netutil.go +++ b/netutil/netutil.go @@ -17,11 +17,19 @@ func PrivateNetworkInterfaces(logger log.Logger) []string { if err != nil { level.Warn(logger).Log("msg", "error getting network interfaces", "err", err) } - return privateNetworkInterfaces(ints, logger) + return privateNetworkInterfaces(ints, []string{}, logger) +} + +func PrivateNetworkInterfacesWithFallback(fallback []string, logger log.Logger) []string { + ints, err := net.Interfaces() + if err != nil { + level.Warn(logger).Log("msg", "error getting network interfaces", "err", err) + } + return privateNetworkInterfaces(ints, fallback, logger) } // private testable function that checks each given interface -func privateNetworkInterfaces(all []net.Interface, logger log.Logger) []string { +func privateNetworkInterfaces(all []net.Interface, fallback []string, logger log.Logger) []string { var privInts []string for _, i := range all { addrs, err := getInterfaceAddrs(&i) @@ -32,7 +40,8 @@ func privateNetworkInterfaces(all []net.Interface, logger log.Logger) []string { s := a.String() ip, _, err := net.ParseCIDR(s) if err != nil { - level.Warn(logger).Log("msg", "error parsing network interface IP address", "inf", i.Name, "addr", s, "err", err) + level.Warn(logger).Log("msg", "error parsing network interface IP address", "interface", i.Name, "addr", s, "err", err) + continue } if ip.IsPrivate() { privInts = append(privInts, i.Name) @@ -41,7 +50,7 @@ func privateNetworkInterfaces(all []net.Interface, logger log.Logger) []string { } } if len(privInts) == 0 { - return []string{"eth0", "en0"} + return fallback } level.Debug(logger).Log("msg", "found network interfaces with private IP addresses assigned", "interfaces", privInts) return privInts diff --git a/netutil/netutil_test.go b/netutil/netutil_test.go index 725bc7b52..72b68f470 100644 --- a/netutil/netutil_test.go +++ b/netutil/netutil_test.go @@ -107,6 +107,7 @@ func TestPrivateInterface(t *testing.T) { t.Run(scenario.description, func(t *testing.T) { privInts := privateNetworkInterfaces( generateTestInterfaces(scenario.interfaces), + defaultOutput, log.NewNopLogger(), ) assert.Equal(t, privInts, scenario.expectedOutput) @@ -121,6 +122,6 @@ func TestPrivateInterfaceError(t *testing.T) { return []net.Addr{mockAddr{netAddr: ipaddr}}, nil } logger := log.NewLogfmtLogger(os.Stdout) - privInts := privateNetworkInterfaces(interfaces, logger) - assert.Equal(t, privInts, []string{"eth0", "en0"}) + privInts := privateNetworkInterfaces(interfaces, []string{}, logger) + assert.Equal(t, privInts, []string{}) } diff --git a/ring/lifecycler.go b/ring/lifecycler.go index 195dfe2af..d0b046c09 100644 --- a/ring/lifecycler.go +++ b/ring/lifecycler.go @@ -54,13 +54,13 @@ type LifecyclerConfig struct { // RegisterFlags adds the flags required to config this to the given FlagSet. // The default values of some flags can be changed; see docs of LifecyclerConfig. -func (cfg *LifecyclerConfig) RegisterFlags(f *flag.FlagSet) { - cfg.RegisterFlagsWithPrefix("", f) +func (cfg *LifecyclerConfig) RegisterFlags(f *flag.FlagSet, logger log.Logger) { + cfg.RegisterFlagsWithPrefix("", f, logger) } // RegisterFlagsWithPrefix adds the flags required to config this to the given FlagSet. // The default values of some flags can be changed; see docs of LifecyclerConfig. -func (cfg *LifecyclerConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet) { +func (cfg *LifecyclerConfig) RegisterFlagsWithPrefix(prefix string, f *flag.FlagSet, logger log.Logger) { cfg.RingConfig.RegisterFlagsWithPrefix(prefix, f) // In order to keep backwards compatibility all of these need to be prefixed @@ -82,7 +82,7 @@ func (cfg *LifecyclerConfig) RegisterFlagsWithPrefix(prefix string, f *flag.Flag panic(fmt.Errorf("failed to get hostname %s", err)) } - cfg.InfNames = netutil.PrivateNetworkInterfaces(log.NewLogfmtLogger(os.Stdout)) + cfg.InfNames = netutil.PrivateNetworkInterfacesWithFallback([]string{"eth0", "en0"}, logger) f.Var((*flagext.StringSlice)(&cfg.InfNames), prefix+"lifecycler.interface", "Name of network interface to read address from.") f.StringVar(&cfg.Addr, prefix+"lifecycler.addr", "", "IP address to advertise in the ring.") f.IntVar(&cfg.Port, prefix+"lifecycler.port", 0, "port to advertise in consul (defaults to server.grpc-listen-port).")