mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-16 19:14:41 -07:00
draft of fix for issue #447. allows advertising separate host:ports to client.
This commit is contained in:
@@ -30,6 +30,7 @@ type ClusterOpts struct {
|
||||
TLSTimeout float64 `json:"-"`
|
||||
TLSConfig *tls.Config `json:"-"`
|
||||
ListenStr string `json:"-"`
|
||||
AdvertiseStr string `json:"-"`
|
||||
NoAdvertise bool `json:"-"`
|
||||
ConnectRetries int `json:"-"`
|
||||
}
|
||||
@@ -387,6 +388,8 @@ func parseCluster(cm map[string]interface{}, opts *Options) error {
|
||||
opts.Cluster.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
opts.Cluster.TLSConfig.RootCAs = opts.Cluster.TLSConfig.ClientCAs
|
||||
opts.Cluster.TLSTimeout = tc.Timeout
|
||||
case "cluster_advertise":
|
||||
opts.Cluster.AdvertiseStr = mv.(string)
|
||||
case "no_advertise":
|
||||
opts.Cluster.NoAdvertise = mv.(bool)
|
||||
case "connect_retries":
|
||||
@@ -753,6 +756,9 @@ func MergeOptions(fileOpts, flagOpts *Options) *Options {
|
||||
if flagOpts.Cluster.ListenStr != "" {
|
||||
opts.Cluster.ListenStr = flagOpts.Cluster.ListenStr
|
||||
}
|
||||
if flagOpts.Cluster.AdvertiseStr != "" {
|
||||
opts.Cluster.AdvertiseStr = flagOpts.Cluster.AdvertiseStr
|
||||
}
|
||||
if flagOpts.Cluster.NoAdvertise {
|
||||
opts.Cluster.NoAdvertise = true
|
||||
}
|
||||
@@ -974,6 +980,7 @@ func ConfigureOptions(fs *flag.FlagSet, args []string, printVersion, printHelp,
|
||||
fs.StringVar(&opts.RoutesStr, "routes", "", "Routes to actively solicit a connection.")
|
||||
fs.StringVar(&opts.Cluster.ListenStr, "cluster", "", "Cluster url from which members can solicit routes.")
|
||||
fs.StringVar(&opts.Cluster.ListenStr, "cluster_listen", "", "Cluster url from which members can solicit routes.")
|
||||
fs.StringVar(&opts.Cluster.AdvertiseStr, "cluster_advertise", "", "Cluster URL sent on info for use with proxied connections.")
|
||||
fs.BoolVar(&opts.Cluster.NoAdvertise, "no_advertise", false, "Advertise known cluster IPs to clients.")
|
||||
fs.IntVar(&opts.Cluster.ConnectRetries, "connect_retries", 0, "For implicit routes, number of connect retries")
|
||||
fs.BoolVar(&showTLSHelp, "help_tls", false, "TLS help.")
|
||||
@@ -1171,6 +1178,7 @@ func overrideCluster(opts *Options) error {
|
||||
opts.Cluster.Username = ""
|
||||
opts.Cluster.Password = ""
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1002,42 +1002,62 @@ func (s *Server) getClientConnectURLs() []string {
|
||||
sPort := strconv.Itoa(opts.Port)
|
||||
urls := make([]string, 0, 1)
|
||||
|
||||
ipAddr, err := net.ResolveIPAddr("ip", opts.Host)
|
||||
// If the host is "any" (0.0.0.0 or ::), get specific IPs from available
|
||||
// interfaces.
|
||||
if err == nil && ipAddr.IP.IsUnspecified() {
|
||||
var ip net.IP
|
||||
ifaces, _ := net.Interfaces()
|
||||
for _, i := range ifaces {
|
||||
addrs, _ := i.Addrs()
|
||||
for _, addr := range addrs {
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
// short circuit if cluster-advertise is set
|
||||
if opts.Cluster.AdvertiseStr != "" {
|
||||
hosts := strings.Split(opts.Cluster.AdvertiseStr, ",")
|
||||
|
||||
for _, i := range hosts {
|
||||
hostPort := strings.Split(i, ":")
|
||||
host := strings.TrimSpace(hostPort[0])
|
||||
|
||||
if len(hostPort) > 1 {
|
||||
port := strings.TrimSpace(hostPort[1])
|
||||
// if a separate advertise port is set, use that. Otherwise, use the main listen port.
|
||||
if port != "" {
|
||||
sPort = port
|
||||
}
|
||||
// Skip non global unicast addresses
|
||||
if !ip.IsGlobalUnicast() || ip.IsUnspecified() {
|
||||
ip = nil
|
||||
continue
|
||||
}
|
||||
urls = append(urls, net.JoinHostPort(host, sPort))
|
||||
}
|
||||
} else {
|
||||
ipAddr, err := net.ResolveIPAddr("ip", opts.Host)
|
||||
// If the host is "any" (0.0.0.0 or ::), get specific IPs from available
|
||||
// interfaces.
|
||||
if err == nil && ipAddr.IP.IsUnspecified() {
|
||||
var ip net.IP
|
||||
ifaces, _ := net.Interfaces()
|
||||
for _, i := range ifaces {
|
||||
addrs, _ := i.Addrs()
|
||||
for _, addr := range addrs {
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ip = v.IP
|
||||
case *net.IPAddr:
|
||||
ip = v.IP
|
||||
}
|
||||
// Skip non global unicast addresses
|
||||
if !ip.IsGlobalUnicast() || ip.IsUnspecified() {
|
||||
ip = nil
|
||||
continue
|
||||
}
|
||||
urls = append(urls, net.JoinHostPort(ip.String(), sPort))
|
||||
}
|
||||
urls = append(urls, net.JoinHostPort(ip.String(), sPort))
|
||||
}
|
||||
}
|
||||
if err != nil || len(urls) == 0 {
|
||||
// We are here if s.opts.Host is not "0.0.0.0" nor "::", or if for some
|
||||
// reason we could not add any URL in the loop above.
|
||||
// We had a case where a Windows VM was hosed and would have err == nil
|
||||
// and not add any address in the array in the loop above, and we
|
||||
// ended-up returning 0.0.0.0, which is problematic for Windows clients.
|
||||
// Check for 0.0.0.0 or :: specifically, and ignore if that's the case.
|
||||
if opts.Host == "0.0.0.0" || opts.Host == "::" {
|
||||
s.Errorf("Address %q can not be resolved properly", opts.Host)
|
||||
} else {
|
||||
urls = append(urls, net.JoinHostPort(opts.Host, sPort))
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil || len(urls) == 0 {
|
||||
// We are here if s.opts.Host is not "0.0.0.0" nor "::", or if for some
|
||||
// reason we could not add any URL in the loop above.
|
||||
// We had a case where a Windows VM was hosed and would have err == nil
|
||||
// and not add any address in the array in the loop above, and we
|
||||
// ended-up returning 0.0.0.0, which is problematic for Windows clients.
|
||||
// Check for 0.0.0.0 or :: specifically, and ignore if that's the case.
|
||||
if opts.Host == "0.0.0.0" || opts.Host == "::" {
|
||||
s.Errorf("Address %q can not be resolved properly", opts.Host)
|
||||
} else {
|
||||
urls = append(urls, net.JoinHostPort(opts.Host, sPort))
|
||||
}
|
||||
}
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
@@ -213,6 +213,46 @@ func TestGetConnectURLs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestClusterAdvertiseConnectURL(t *testing.T) {
|
||||
opts := DefaultOptions()
|
||||
opts.Port = 4222
|
||||
opts.Cluster.AdvertiseStr = "nats.example.com"
|
||||
|
||||
s := New(opts)
|
||||
|
||||
urls := s.getClientConnectURLs()
|
||||
|
||||
if len(urls) != 1 {
|
||||
t.Fatalf("Expected to get one url, got none: %v with Cluster.AdvertiseStr %v",
|
||||
opts.Host, opts.Cluster.AdvertiseStr)
|
||||
}
|
||||
|
||||
if urls[0] != "nats.example.com:4222" {
|
||||
t.Fatalf("Expected to get '%s', got: '%v'", "nats.example.com:4222", urls[0])
|
||||
}
|
||||
s.Shutdown()
|
||||
|
||||
opts.Cluster.AdvertiseStr = "nats.example.com, nats2.example.com:7777"
|
||||
|
||||
s = New(opts)
|
||||
|
||||
urls = s.getClientConnectURLs()
|
||||
|
||||
if len(urls) != 2 {
|
||||
t.Fatalf("Expected to get two urls, got %d: %v", len(urls), opts.Cluster.AdvertiseStr)
|
||||
}
|
||||
|
||||
if urls[0] != "nats.example.com:4222" {
|
||||
t.Fatalf("Expected 'nats.example.com:4222', got: '%v'", urls[0])
|
||||
}
|
||||
|
||||
if urls[1] != "nats2.example.com:7777" {
|
||||
t.Fatalf("Expected 'nats2.example.com:7777', got: '%v'", urls[1])
|
||||
}
|
||||
|
||||
s.Shutdown()
|
||||
}
|
||||
|
||||
func TestNoDeadlockOnStartFailure(t *testing.T) {
|
||||
opts := DefaultOptions()
|
||||
opts.Host = "x.x.x.x" // bad host
|
||||
|
||||
Reference in New Issue
Block a user