Skip to content

Commit

Permalink
Merge pull request #6170 from heyitsanthony/default-advertise-ip
Browse files Browse the repository at this point in the history
use default ip for advertise URL
  • Loading branch information
Anthony Romano authored Aug 15, 2016
2 parents 7b84456 + 2cc245e commit 16b2d9c
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 8 deletions.
43 changes: 36 additions & 7 deletions embed/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/coreos/etcd/discovery"
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/pkg/cors"
"github.com/coreos/etcd/pkg/netutil"
"github.com/coreos/etcd/pkg/transport"
"github.com/coreos/etcd/pkg/types"
"github.com/ghodss/yaml"
Expand All @@ -33,13 +34,9 @@ const (
ClusterStateFlagNew = "new"
ClusterStateFlagExisting = "existing"

DefaultName = "default"
DefaultInitialAdvertisePeerURLs = "http://localhost:2380"
DefaultAdvertiseClientURLs = "http://localhost:2379"
DefaultListenPeerURLs = "http://localhost:2380"
DefaultListenClientURLs = "http://localhost:2379"
DefaultMaxSnapshots = 5
DefaultMaxWALs = 5
DefaultName = "default"
DefaultMaxSnapshots = 5
DefaultMaxWALs = 5

// maxElectionMs specifies the maximum value of election timeout.
// More details are listed in ../Documentation/tuning.md#time-parameters.
Expand All @@ -50,8 +47,28 @@ var (
ErrConflictBootstrapFlags = fmt.Errorf("multiple discovery or bootstrap flags are set. " +
"Choose one of \"initial-cluster\", \"discovery\" or \"discovery-srv\"")
ErrUnsetAdvertiseClientURLsFlag = fmt.Errorf("--advertise-client-urls is required when --listen-client-urls is set explicitly")

DefaultListenPeerURLs = "http://localhost:2380"
DefaultListenClientURLs = "http://localhost:2379"
DefaultInitialAdvertisePeerURLs = "http://localhost:2380"
DefaultAdvertiseClientURLs = "http://localhost:2379"

defaultHostname string = "localhost"
defaultHostStatus error
)

func init() {
ip, err := netutil.GetDefaultHost()
if err != nil {
defaultHostStatus = err
return
}
// found default host, advertise on it
DefaultInitialAdvertisePeerURLs = "http://" + ip + ":2380"
DefaultAdvertiseClientURLs = "http://" + ip + ":2379"
defaultHostname = ip
}

// Config holds the arguments for configuring an etcd server.
type Config struct {
// member
Expand Down Expand Up @@ -317,3 +334,15 @@ func (cfg Config) InitialClusterFromName(name string) (ret string) {

func (cfg Config) IsNewCluster() bool { return cfg.ClusterState == ClusterStateFlagNew }
func (cfg Config) ElectionTicks() int { return int(cfg.ElectionMs / cfg.TickMs) }

// IsDefaultHost returns the default hostname, if used, and the error, if any,
// from getting the machine's default host.
func (cfg Config) IsDefaultHost() (string, error) {
if len(cfg.APUrls) == 1 && cfg.APUrls[0].String() == DefaultInitialAdvertisePeerURLs {
return defaultHostname, defaultHostStatus
}
if len(cfg.ACUrls) == 1 && cfg.ACUrls[0].String() == DefaultAdvertiseClientURLs {
return defaultHostname, defaultHostStatus
}
return "", defaultHostStatus
}
15 changes: 14 additions & 1 deletion etcdmain/etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ func startEtcdOrProxyV2() {
grpc.EnableTracing = false

cfg := newConfig()
defaultInitialCluster := cfg.InitialCluster

err := cfg.parse(os.Args[1:])
if err != nil {
plog.Errorf("error verifying flags, %v. See 'etcd --help'.", err)
Expand All @@ -83,7 +85,9 @@ func startEtcdOrProxyV2() {
plog.Infof("setting maximum number of CPUs to %d, total number of available CPUs is %d", GoMaxProcs, runtime.NumCPU())

// TODO: check whether fields are set instead of whether fields have default value
if cfg.Name != embed.DefaultName && cfg.InitialCluster == cfg.InitialClusterFromName(embed.DefaultName) {
defaultHost, defaultHostErr := cfg.IsDefaultHost()
defaultHostOverride := defaultHost == "" || defaultHostErr == nil
if (defaultHostOverride || cfg.Name != embed.DefaultName) && cfg.InitialCluster == defaultInitialCluster {
cfg.InitialCluster = cfg.InitialClusterFromName(cfg.Name)
}

Expand Down Expand Up @@ -184,6 +188,15 @@ func startEtcdOrProxyV2() {

// startEtcd runs StartEtcd in addition to hooks needed for standalone etcd.
func startEtcd(cfg *embed.Config) (<-chan struct{}, <-chan error, error) {
defaultHost, dhErr := cfg.IsDefaultHost()
if defaultHost != "" {
if dhErr == nil {
plog.Infof("advertising using detected default host %q", defaultHost)
} else {
plog.Noticef("failed to detect default host, advertise falling back to %q (%v)", defaultHost, dhErr)
}
}

e, err := embed.StartEtcd(cfg)
if err != nil {
return nil, nil, err
Expand Down
28 changes: 28 additions & 0 deletions pkg/netutil/routes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !linux !386,!amd64

package netutil

import (
"fmt"
"runtime"
)

// GetDefaultHost fetches the a resolvable name that corresponds
// to the machine's default routable interface
func GetDefaultHost() (string, error) {
return "", fmt.Errorf("default host not supported on %s_%s", runtime.GOOS, runtime.GOARCH)
}
133 changes: 133 additions & 0 deletions pkg/netutil/routes_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 2016 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build linux
// +build 386 amd64

// TODO support native endian but without using "unsafe"

package netutil

import (
"bytes"
"encoding/binary"
"fmt"
"net"
"syscall"
)

var errNoDefaultRoute = fmt.Errorf("could not find default route")

func GetDefaultHost() (string, error) {
rmsg, rerr := getDefaultRoute()
if rerr != nil {
return "", rerr
}

attrs, aerr := syscall.ParseNetlinkRouteAttr(rmsg)
if aerr != nil {
return "", aerr
}

oif := uint32(0)
for _, attr := range attrs {
if attr.Attr.Type == syscall.RTA_PREFSRC {
return net.IP(attr.Value).String(), nil
}
if attr.Attr.Type == syscall.RTA_OIF {
oif = binary.LittleEndian.Uint32(attr.Value)
}
}

if oif == 0 {
return "", errNoDefaultRoute
}

// prefsrc not detected, fall back to getting address from iface

ifmsg, ierr := getIface(oif)
if ierr != nil {
return "", ierr
}

attrs, aerr = syscall.ParseNetlinkRouteAttr(ifmsg)
if aerr != nil {
return "", aerr
}

for _, attr := range attrs {
if attr.Attr.Type == syscall.RTA_SRC {
return net.IP(attr.Value).String(), nil
}
}

return "", errNoDefaultRoute
}

func getDefaultRoute() (*syscall.NetlinkMessage, error) {
dat, err := syscall.NetlinkRIB(syscall.RTM_GETROUTE, syscall.AF_UNSPEC)
if err != nil {
return nil, err
}

msgs, msgErr := syscall.ParseNetlinkMessage(dat)
if msgErr != nil {
return nil, msgErr
}

rtmsg := syscall.RtMsg{}
for _, m := range msgs {
if m.Header.Type != syscall.RTM_NEWROUTE {
continue
}
buf := bytes.NewBuffer(m.Data[:syscall.SizeofRtMsg])
if rerr := binary.Read(buf, binary.LittleEndian, &rtmsg); rerr != nil {
continue
}
if rtmsg.Dst_len == 0 {
// zero-length Dst_len implies default route
return &m, nil
}
}

return nil, errNoDefaultRoute
}

func getIface(idx uint32) (*syscall.NetlinkMessage, error) {
dat, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
if err != nil {
return nil, err
}

msgs, msgErr := syscall.ParseNetlinkMessage(dat)
if msgErr != nil {
return nil, msgErr
}

ifaddrmsg := syscall.IfAddrmsg{}
for _, m := range msgs {
if m.Header.Type != syscall.RTM_NEWADDR {
continue
}
buf := bytes.NewBuffer(m.Data[:syscall.SizeofIfAddrmsg])
if rerr := binary.Read(buf, binary.LittleEndian, &ifaddrmsg); rerr != nil {
continue
}
if ifaddrmsg.Index == idx {
return &m, nil
}
}

return nil, errNoDefaultRoute
}

0 comments on commit 16b2d9c

Please sign in to comment.