Enhancing --cluster-advertise to support <interface-name>
--cluster-advertise daemon option is enahanced to support <interface-name> in addition to <ip-address> in order to amke it automation friendly using docker-machine. Signed-off-by: Madhu Venugopal <madhu@docker.com>
This commit is contained in:
parent
80439a4ce2
commit
3e7db73b99
7 changed files with 121 additions and 4 deletions
|
@ -107,5 +107,8 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
|
||||||
fmt.Fprintf(cli.out, "Cluster store: %s\n", info.ClusterStore)
|
fmt.Fprintf(cli.out, "Cluster store: %s\n", info.ClusterStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if info.ClusterAdvertise != "" {
|
||||||
|
fmt.Fprintf(cli.out, "Cluster advertise: %s\n", info.ClusterAdvertise)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,6 +219,7 @@ type Info struct {
|
||||||
ExperimentalBuild bool
|
ExperimentalBuild bool
|
||||||
ServerVersion string
|
ServerVersion string
|
||||||
ClusterStore string
|
ClusterStore string
|
||||||
|
ClusterAdvertise string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecStartCheck is a temp struct used by execStart
|
// ExecStartCheck is a temp struct used by execStart
|
||||||
|
|
|
@ -71,7 +71,7 @@ func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string)
|
||||||
cmd.Var(opts.NewListOptsRef(&config.Labels, opts.ValidateLabel), []string{"-label"}, usageFn("Set key=value labels to the daemon"))
|
cmd.Var(opts.NewListOptsRef(&config.Labels, opts.ValidateLabel), []string{"-label"}, usageFn("Set key=value labels to the daemon"))
|
||||||
cmd.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", usageFn("Default driver for container logs"))
|
cmd.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", usageFn("Default driver for container logs"))
|
||||||
cmd.Var(opts.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options"))
|
cmd.Var(opts.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options"))
|
||||||
cmd.StringVar(&config.ClusterAdvertise, []string{"-cluster-advertise"}, "", usageFn("Address of the daemon instance to advertise"))
|
cmd.StringVar(&config.ClusterAdvertise, []string{"-cluster-advertise"}, "", usageFn("Address or interface name to advertise"))
|
||||||
cmd.StringVar(&config.ClusterStore, []string{"-cluster-store"}, "", usageFn("Set the cluster store"))
|
cmd.StringVar(&config.ClusterStore, []string{"-cluster-store"}, "", usageFn("Set the cluster store"))
|
||||||
cmd.Var(opts.NewMapOpts(config.ClusterOpts, nil), []string{"-cluster-store-opt"}, usageFn("Set cluster store options"))
|
cmd.Var(opts.NewMapOpts(config.ClusterOpts, nil), []string{"-cluster-store-opt"}, usageFn("Set cluster store options"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -767,10 +767,17 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
|
||||||
// initialized, the daemon is registered and we can store the discovery backend as its read-only
|
// initialized, the daemon is registered and we can store the discovery backend as its read-only
|
||||||
// DiscoveryWatcher version.
|
// DiscoveryWatcher version.
|
||||||
if config.ClusterStore != "" && config.ClusterAdvertise != "" {
|
if config.ClusterStore != "" && config.ClusterAdvertise != "" {
|
||||||
var err error
|
advertise, err := discovery.ParseAdvertise(config.ClusterStore, config.ClusterAdvertise)
|
||||||
if d.discoveryWatcher, err = initDiscovery(config.ClusterStore, config.ClusterAdvertise, config.ClusterOpts); err != nil {
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("discovery advertise parsing failed (%v)", err)
|
||||||
|
}
|
||||||
|
config.ClusterAdvertise = advertise
|
||||||
|
d.discoveryWatcher, err = initDiscovery(config.ClusterStore, config.ClusterAdvertise, config.ClusterOpts)
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("discovery initialization failed (%v)", err)
|
return nil, fmt.Errorf("discovery initialization failed (%v)", err)
|
||||||
}
|
}
|
||||||
|
} else if config.ClusterAdvertise != "" {
|
||||||
|
return nil, fmt.Errorf("invalid cluster configuration. --cluster-advertise must be accompanied by --cluster-store configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
d.netController, err = d.initNetworkController(config)
|
d.netController, err = d.initNetworkController(config)
|
||||||
|
|
|
@ -92,6 +92,7 @@ func (daemon *Daemon) SystemInfo() (*types.Info, error) {
|
||||||
ExperimentalBuild: utils.ExperimentalBuild(),
|
ExperimentalBuild: utils.ExperimentalBuild(),
|
||||||
ServerVersion: dockerversion.VERSION,
|
ServerVersion: dockerversion.VERSION,
|
||||||
ClusterStore: daemon.config().ClusterStore,
|
ClusterStore: daemon.config().ClusterStore,
|
||||||
|
ClusterAdvertise: daemon.config().ClusterAdvertise,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Windows. Refactor this more once sysinfo is refactored into
|
// TODO Windows. Refactor this more once sysinfo is refactored into
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/integration/checker"
|
"github.com/docker/docker/pkg/integration/checker"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
|
@ -42,11 +43,57 @@ func (s *DockerSuite) TestInfoDiscoveryBackend(c *check.C) {
|
||||||
|
|
||||||
d := NewDaemon(c)
|
d := NewDaemon(c)
|
||||||
discoveryBackend := "consul://consuladdr:consulport/some/path"
|
discoveryBackend := "consul://consuladdr:consulport/some/path"
|
||||||
err := d.Start(fmt.Sprintf("--cluster-store=%s", discoveryBackend), "--cluster-advertise=foo")
|
discoveryAdvertise := "1.1.1.1:2375"
|
||||||
|
err := d.Start(fmt.Sprintf("--cluster-store=%s", discoveryBackend), fmt.Sprintf("--cluster-advertise=%s", discoveryAdvertise))
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
defer d.Stop()
|
defer d.Stop()
|
||||||
|
|
||||||
out, err := d.Cmd("info")
|
out, err := d.Cmd("info")
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(out, checker.Contains, fmt.Sprintf("Cluster store: %s\n", discoveryBackend))
|
c.Assert(out, checker.Contains, fmt.Sprintf("Cluster store: %s\n", discoveryBackend))
|
||||||
|
c.Assert(out, checker.Contains, fmt.Sprintf("Cluster advertise: %s\n", discoveryAdvertise))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestInfoDiscoveryInvalidAdvertise verifies that a daemon run with
|
||||||
|
// an invalid `--cluster-advertise` configuration
|
||||||
|
func (s *DockerSuite) TestInfoDiscoveryInvalidAdvertise(c *check.C) {
|
||||||
|
testRequires(c, SameHostDaemon)
|
||||||
|
|
||||||
|
d := NewDaemon(c)
|
||||||
|
discoveryBackend := "consul://consuladdr:consulport/some/path"
|
||||||
|
|
||||||
|
// --cluster-advertise with an invalid string is an error
|
||||||
|
err := d.Start(fmt.Sprintf("--cluster-store=%s", discoveryBackend), "--cluster-advertise=invalid")
|
||||||
|
c.Assert(err, checker.Not(checker.IsNil))
|
||||||
|
|
||||||
|
// --cluster-advertise without --cluster-store is also an error
|
||||||
|
err = d.Start("--cluster-advertise=1.1.1.1:2375")
|
||||||
|
c.Assert(err, checker.Not(checker.IsNil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestInfoDiscoveryAdvertiseInterfaceName verifies that a daemon run with `--cluster-advertise`
|
||||||
|
// configured with interface name properly show the advertise ip-address in info output.
|
||||||
|
func (s *DockerSuite) TestInfoDiscoveryAdvertiseInterfaceName(c *check.C) {
|
||||||
|
testRequires(c, SameHostDaemon)
|
||||||
|
|
||||||
|
d := NewDaemon(c)
|
||||||
|
discoveryBackend := "consul://consuladdr:consulport/some/path"
|
||||||
|
discoveryAdvertise := "eth0"
|
||||||
|
|
||||||
|
err := d.Start(fmt.Sprintf("--cluster-store=%s", discoveryBackend), fmt.Sprintf("--cluster-advertise=%s:2375", discoveryAdvertise))
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
defer d.Stop()
|
||||||
|
|
||||||
|
iface, err := net.InterfaceByName(discoveryAdvertise)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(len(addrs), checker.GreaterThan, 0)
|
||||||
|
ip, _, err := net.ParseCIDR(addrs[0].String())
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
out, err := d.Cmd("info")
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(out, checker.Contains, fmt.Sprintf("Cluster store: %s\n", discoveryBackend))
|
||||||
|
c.Assert(out, checker.Contains, fmt.Sprintf("Cluster advertise: %s:2375\n", ip.String()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package discovery
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -39,6 +40,63 @@ func parse(rawurl string) (string, string) {
|
||||||
return parts[0], parts[1]
|
return parts[0], parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseAdvertise parses the --cluster-advertise daemon config which accepts
|
||||||
|
// <ip-address>:<port> or <interface-name>:<port>
|
||||||
|
func ParseAdvertise(store, advertise string) (string, error) {
|
||||||
|
var (
|
||||||
|
iface *net.Interface
|
||||||
|
addrs []net.Addr
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
addr, port, err := net.SplitHostPort(advertise)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid --cluster-advertise configuration: %s: %v", advertise, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP(addr)
|
||||||
|
// If it is a valid ip-address, use it as is
|
||||||
|
if ip != nil {
|
||||||
|
return advertise, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If advertise is a valid interface name, get the valid ipv4 address and use it to advertise
|
||||||
|
ifaceName := addr
|
||||||
|
iface, err = net.InterfaceByName(ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid cluster advertise IP address or interface name (%s) : %v", advertise, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err = iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to get advertise IP address from interface (%s) : %v", advertise, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if addrs == nil || len(addrs) == 0 {
|
||||||
|
return "", fmt.Errorf("no available advertise IP address in interface (%s)", advertise)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = ""
|
||||||
|
for _, a := range addrs {
|
||||||
|
ip, _, err := net.ParseCIDR(a.String())
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error deriving advertise ip-address in interface (%s) : %v", advertise, err)
|
||||||
|
}
|
||||||
|
if ip.To4() == nil || ip.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addr = ip.String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if addr == "" {
|
||||||
|
return "", fmt.Errorf("couldnt find a valid ip-address in interface %s", advertise)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = fmt.Sprintf("%s:%s", addr, port)
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
// New returns a new Discovery given a URL, heartbeat and ttl settings.
|
// New returns a new Discovery given a URL, heartbeat and ttl settings.
|
||||||
// Returns an error if the URL scheme is not supported.
|
// Returns an error if the URL scheme is not supported.
|
||||||
func New(rawurl string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) (Backend, error) {
|
func New(rawurl string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) (Backend, error) {
|
||||||
|
|
Loading…
Reference in a new issue