Add rpc_listener option to segment config
This commit is contained in:
parent
e582d02079
commit
107d7f6c5a
|
@ -648,30 +648,12 @@ func (a *Agent) consulConfig() (*consul.Config, error) {
|
||||||
base.RPCAdvertise = a.config.AdvertiseAddrs.RPC
|
base.RPCAdvertise = a.config.AdvertiseAddrs.RPC
|
||||||
}
|
}
|
||||||
base.Segment = a.config.Segment
|
base.Segment = a.config.Segment
|
||||||
for _, segment := range a.config.Segments {
|
if len(a.config.Segments) > 0 {
|
||||||
config := consul.DefaultConfig().SerfLANConfig
|
segments, err := a.segmentConfig()
|
||||||
|
if err != nil {
|
||||||
config.MemberlistConfig.AdvertiseAddr = segment.Advertise
|
return nil, err
|
||||||
config.MemberlistConfig.AdvertisePort = segment.Port
|
|
||||||
config.MemberlistConfig.BindAddr = segment.Bind
|
|
||||||
config.MemberlistConfig.BindPort = segment.Port
|
|
||||||
if a.config.ReconnectTimeoutLan != 0 {
|
|
||||||
config.ReconnectTimeout = a.config.ReconnectTimeoutLan
|
|
||||||
}
|
}
|
||||||
if a.config.EncryptVerifyIncoming != nil {
|
base.Segments = segments
|
||||||
config.MemberlistConfig.GossipVerifyIncoming = *a.config.EncryptVerifyIncoming
|
|
||||||
}
|
|
||||||
if a.config.EncryptVerifyOutgoing != nil {
|
|
||||||
config.MemberlistConfig.GossipVerifyOutgoing = *a.config.EncryptVerifyOutgoing
|
|
||||||
}
|
|
||||||
|
|
||||||
base.Segments = append(base.Segments, consul.NetworkSegment{
|
|
||||||
Name: segment.Name,
|
|
||||||
Bind: segment.Bind,
|
|
||||||
Port: segment.Port,
|
|
||||||
Advertise: segment.Advertise,
|
|
||||||
SerfConfig: config,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
if a.config.Bootstrap {
|
if a.config.Bootstrap {
|
||||||
base.Bootstrap = true
|
base.Bootstrap = true
|
||||||
|
@ -789,6 +771,49 @@ func (a *Agent) consulConfig() (*consul.Config, error) {
|
||||||
return base, nil
|
return base, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup the serf and memberlist config for any defined network segments.
|
||||||
|
func (a *Agent) segmentConfig() ([]consul.NetworkSegment, error) {
|
||||||
|
var segments []consul.NetworkSegment
|
||||||
|
config := a.config
|
||||||
|
|
||||||
|
for _, segment := range config.Segments {
|
||||||
|
serfConf := consul.DefaultConfig().SerfLANConfig
|
||||||
|
|
||||||
|
serfConf.MemberlistConfig.AdvertiseAddr = segment.Advertise
|
||||||
|
serfConf.MemberlistConfig.AdvertisePort = segment.Port
|
||||||
|
serfConf.MemberlistConfig.BindAddr = segment.Bind
|
||||||
|
serfConf.MemberlistConfig.BindPort = segment.Port
|
||||||
|
if config.ReconnectTimeoutLan != 0 {
|
||||||
|
serfConf.ReconnectTimeout = config.ReconnectTimeoutLan
|
||||||
|
}
|
||||||
|
if config.EncryptVerifyIncoming != nil {
|
||||||
|
serfConf.MemberlistConfig.GossipVerifyIncoming = *config.EncryptVerifyIncoming
|
||||||
|
}
|
||||||
|
if config.EncryptVerifyOutgoing != nil {
|
||||||
|
serfConf.MemberlistConfig.GossipVerifyOutgoing = *config.EncryptVerifyOutgoing
|
||||||
|
}
|
||||||
|
|
||||||
|
var rpcAddr *net.TCPAddr
|
||||||
|
if segment.RPCListener {
|
||||||
|
rpcAddr = &net.TCPAddr{
|
||||||
|
IP: net.ParseIP(segment.Bind),
|
||||||
|
Port: a.config.Ports.Server,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
segments = append(segments, consul.NetworkSegment{
|
||||||
|
Name: segment.Name,
|
||||||
|
Bind: segment.Bind,
|
||||||
|
Port: segment.Port,
|
||||||
|
Advertise: segment.Advertise,
|
||||||
|
RPCAddr: rpcAddr,
|
||||||
|
SerfConfig: serfConf,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return segments, nil
|
||||||
|
}
|
||||||
|
|
||||||
// makeRandomID will generate a random UUID for a node.
|
// makeRandomID will generate a random UUID for a node.
|
||||||
func (a *Agent) makeRandomID() (string, error) {
|
func (a *Agent) makeRandomID() (string, error) {
|
||||||
id, err := uuid.GenerateUUID()
|
id, err := uuid.GenerateUUID()
|
||||||
|
|
|
@ -357,11 +357,16 @@ type NetworkSegment struct {
|
||||||
Name string `mapstructure:"name"`
|
Name string `mapstructure:"name"`
|
||||||
|
|
||||||
// Bind is the bind address for this segment.
|
// Bind is the bind address for this segment.
|
||||||
Bind string `mapstructure:"bind"`
|
Bind string `mapstructure:"bind"`
|
||||||
|
BindAddrs []string `mapstructure:"-"`
|
||||||
|
|
||||||
// Port is the port for this segment.
|
// Port is the port for this segment.
|
||||||
Port int `mapstructure:"port"`
|
Port int `mapstructure:"port"`
|
||||||
|
|
||||||
|
// RPCListener is whether to bind a separate RPC listener on the bind address
|
||||||
|
// for this segment.
|
||||||
|
RPCListener bool `mapstructure:"rpc_listener"`
|
||||||
|
|
||||||
// Advertise is the advertise address of this segment.
|
// Advertise is the advertise address of this segment.
|
||||||
Advertise string `mapstructure:"advertise"`
|
Advertise string `mapstructure:"advertise"`
|
||||||
}
|
}
|
||||||
|
@ -1408,6 +1413,11 @@ func DecodeConfig(r io.Reader) (*Config, error) {
|
||||||
result.AdvertiseAddrs.RPC = addr
|
result.AdvertiseAddrs.RPC = addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate segment config.
|
||||||
|
if err := ValidateSegments(&result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Enforce the max Raft multiplier.
|
// Enforce the max Raft multiplier.
|
||||||
if result.Performance.RaftMultiplier > consul.MaxRaftMultiplier {
|
if result.Performance.RaftMultiplier > consul.MaxRaftMultiplier {
|
||||||
return nil, fmt.Errorf("Performance.RaftMultiplier must be <= %d", consul.MaxRaftMultiplier)
|
return nil, fmt.Errorf("Performance.RaftMultiplier must be <= %d", consul.MaxRaftMultiplier)
|
||||||
|
@ -1461,31 +1471,25 @@ func DecodeConfig(r io.Reader) (*Config, error) {
|
||||||
return nil, fmt.Errorf("Failed to parse node metadata: %v", err)
|
return nil, fmt.Errorf("Failed to parse node metadata: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate segment config
|
|
||||||
if err := ValidateSegments(&result); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateSegments(conf *Config) error {
|
func ValidateSegments(conf *Config) error {
|
||||||
if conf.Server && conf.Segment != "" {
|
|
||||||
return fmt.Errorf("Segment option can only be set on clients")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !conf.Server && len(conf.Segments) > 0 {
|
|
||||||
return fmt.Errorf("Cannot define segments on clients")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(conf.Segments) > SegmentLimit {
|
if len(conf.Segments) > SegmentLimit {
|
||||||
return fmt.Errorf("Cannot exceed network segment limit of %d", SegmentLimit)
|
return fmt.Errorf("Cannot exceed network segment limit of %d", SegmentLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
takenPorts := make(map[int]string, len(conf.Segments))
|
||||||
for _, segment := range conf.Segments {
|
for _, segment := range conf.Segments {
|
||||||
if len(segment.Name) > SegmentNameLimit {
|
if len(segment.Name) > SegmentNameLimit {
|
||||||
return fmt.Errorf("Segment name %q exceeds maximum length of %d", segment.Name, SegmentNameLimit)
|
return fmt.Errorf("Segment name %q exceeds maximum length of %d", segment.Name, SegmentNameLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
previous, ok := takenPorts[segment.Port]
|
||||||
|
if ok {
|
||||||
|
return fmt.Errorf("Segment %q port %d overlaps with segment %q", segment.Name, segment.Port, previous)
|
||||||
|
}
|
||||||
|
takenPorts[segment.Port] = segment.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -2275,7 +2279,6 @@ func (c *Config) ResolveTmplAddrs() (err error) {
|
||||||
for i, segment := range c.Segments {
|
for i, segment := range c.Segments {
|
||||||
parse(&c.Segments[i].Bind, false, fmt.Sprintf("Segment %q bind address", segment.Name))
|
parse(&c.Segments[i].Bind, false, fmt.Sprintf("Segment %q bind address", segment.Name))
|
||||||
parse(&c.Segments[i].Advertise, false, fmt.Sprintf("Segment %q advertise address", segment.Name))
|
parse(&c.Segments[i].Advertise, false, fmt.Sprintf("Segment %q advertise address", segment.Name))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -597,8 +597,15 @@ func TestDecodeConfig(t *testing.T) {
|
||||||
c: &Config{Segment: "thing"},
|
c: &Config{Segment: "thing"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: `{"segments":[{"name": "alpha", "bind": "127.0.0.1", "port": 1234, "advertise": "1.1.1.1"}]}`,
|
in: `{"server": true, "segments":[{"name": "alpha", "bind": "127.0.0.1", "port": 1234, "rpc_listener": true, "advertise": "1.1.1.1"}]}`,
|
||||||
c: &Config{Segments: []NetworkSegment{{Name: "alpha", Bind: "127.0.0.1", Port: 1234, Advertise: "1.1.1.1"}}},
|
c: &Config{Server: true, Segments: []NetworkSegment{{
|
||||||
|
Name: "alpha",
|
||||||
|
Bind: "127.0.0.1",
|
||||||
|
BindAddrs: []string{"127.0.0.1"},
|
||||||
|
Port: 1234,
|
||||||
|
RPCListener: true,
|
||||||
|
Advertise: "1.1.1.1",
|
||||||
|
}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
in: `{"serf_lan_bind":"1.2.3.4"}`,
|
in: `{"serf_lan_bind":"1.2.3.4"}`,
|
||||||
|
@ -1311,18 +1318,6 @@ func TestDecodeConfig_VerifyUniqueListeners(t *testing.T) {
|
||||||
|
|
||||||
func TestDecodeConfig_ValidateSegments(t *testing.T) {
|
func TestDecodeConfig_ValidateSegments(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
serverWithSegment := &Config{Segment: "asfd", Server: true}
|
|
||||||
if err := ValidateSegments(serverWithSegment); !strings.Contains(err.Error(), "can only be set on clients") {
|
|
||||||
t.Fatalf("bad: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
clientWithSegments := &Config{
|
|
||||||
Segments: []NetworkSegment{{Name: "asdf"}},
|
|
||||||
}
|
|
||||||
if err := ValidateSegments(clientWithSegments); !strings.Contains(err.Error(), "Cannot define segments on clients") {
|
|
||||||
t.Fatalf("bad: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tooManySegments := &Config{Server: true}
|
tooManySegments := &Config{Server: true}
|
||||||
for i := 0; i < SegmentLimit+1; i++ {
|
for i := 0; i < SegmentLimit+1; i++ {
|
||||||
tooManySegments.Segments = append(tooManySegments.Segments, NetworkSegment{})
|
tooManySegments.Segments = append(tooManySegments.Segments, NetworkSegment{})
|
||||||
|
@ -1338,6 +1333,17 @@ func TestDecodeConfig_ValidateSegments(t *testing.T) {
|
||||||
if err := ValidateSegments(segmentNameTooLong); !strings.Contains(err.Error(), "exceeds maximum length") {
|
if err := ValidateSegments(segmentNameTooLong); !strings.Contains(err.Error(), "exceeds maximum length") {
|
||||||
t.Fatalf("bad: %v", err)
|
t.Fatalf("bad: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
duplicatePorts := &Config{
|
||||||
|
Segments: []NetworkSegment{
|
||||||
|
{Name: "asdf", Port: 1234},
|
||||||
|
{Name: "qwer", Port: 1234},
|
||||||
|
},
|
||||||
|
Server: true,
|
||||||
|
}
|
||||||
|
if err := ValidateSegments(duplicatePorts); !strings.Contains(err.Error(), "port 1234 overlaps with segment \"asdf\"") {
|
||||||
|
t.Fatalf("bad: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefaultConfig(t *testing.T) {
|
func TestDefaultConfig(t *testing.T) {
|
||||||
|
|
|
@ -55,6 +55,7 @@ type NetworkSegment struct {
|
||||||
Bind string
|
Bind string
|
||||||
Port int
|
Port int
|
||||||
Advertise string
|
Advertise string
|
||||||
|
RPCAddr *net.TCPAddr
|
||||||
SerfConfig *serf.Config
|
SerfConfig *serf.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,10 +46,10 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// listen is used to listen for incoming RPC connections
|
// listen is used to listen for incoming RPC connections
|
||||||
func (s *Server) listen() {
|
func (s *Server) listen(listener net.Listener) {
|
||||||
for {
|
for {
|
||||||
// Accept a connection
|
// Accept a connection
|
||||||
conn, err := s.Listener.Accept()
|
conn, err := listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if s.shutdown {
|
if s.shutdown {
|
||||||
return
|
return
|
||||||
|
|
|
@ -167,6 +167,9 @@ type Server struct {
|
||||||
segmentLAN map[string]*serf.Serf
|
segmentLAN map[string]*serf.Serf
|
||||||
segmentLock sync.RWMutex
|
segmentLock sync.RWMutex
|
||||||
|
|
||||||
|
// segmentListeners holds the RPC listener for any segment with a separate listener.
|
||||||
|
segmentListeners map[string]net.Listener
|
||||||
|
|
||||||
// serfWAN is the Serf cluster maintained between DC's
|
// serfWAN is the Serf cluster maintained between DC's
|
||||||
// which SHOULD only consist of Consul servers
|
// which SHOULD only consist of Consul servers
|
||||||
serfWAN *serf.Serf
|
serfWAN *serf.Serf
|
||||||
|
@ -306,6 +309,7 @@ func NewServerLogger(config *Config, logger *log.Logger, tokens *token.Store) (*
|
||||||
rpcTLS: incomingTLS,
|
rpcTLS: incomingTLS,
|
||||||
reassertLeaderCh: make(chan chan error),
|
reassertLeaderCh: make(chan chan error),
|
||||||
segmentLAN: make(map[string]*serf.Serf, len(config.Segments)),
|
segmentLAN: make(map[string]*serf.Serf, len(config.Segments)),
|
||||||
|
segmentListeners: make(map[string]net.Listener),
|
||||||
sessionTimers: NewSessionTimers(),
|
sessionTimers: NewSessionTimers(),
|
||||||
tombstoneGC: gc,
|
tombstoneGC: gc,
|
||||||
serverLookup: NewServerLookup(),
|
serverLookup: NewServerLookup(),
|
||||||
|
@ -418,7 +422,12 @@ func NewServerLogger(config *Config, logger *log.Logger, tokens *token.Store) (*
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start listening for RPC requests.
|
// Start listening for RPC requests.
|
||||||
go s.listen()
|
go s.listen(s.Listener)
|
||||||
|
|
||||||
|
// Start listeners for any segments with separate RPC listeners.
|
||||||
|
for _, listener := range s.segmentListeners {
|
||||||
|
go s.listen(listener)
|
||||||
|
}
|
||||||
|
|
||||||
// Start the metrics handlers.
|
// Start the metrics handlers.
|
||||||
go s.sessionStats()
|
go s.sessionStats()
|
||||||
|
@ -645,6 +654,19 @@ func (s *Server) setupRPC(tlsWrap tlsutil.DCWrapper) error {
|
||||||
return fmt.Errorf("RPC advertise address is not advertisable: %v", s.config.RPCAdvertise)
|
return fmt.Errorf("RPC advertise address is not advertisable: %v", s.config.RPCAdvertise)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, segment := range s.config.Segments {
|
||||||
|
if segment.RPCAddr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentListener, err := net.ListenTCP("tcp", segment.RPCAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.segmentListeners[segment.Name] = segmentListener
|
||||||
|
}
|
||||||
|
|
||||||
// Provide a DC specific wrapper. Raft replication is only
|
// Provide a DC specific wrapper. Raft replication is only
|
||||||
// ever done in the same datacenter, so we can provide it as a constant.
|
// ever done in the same datacenter, so we can provide it as a constant.
|
||||||
wrapper := tlsutil.SpecificDC(s.config.Datacenter, tlsWrap)
|
wrapper := tlsutil.SpecificDC(s.config.Datacenter, tlsWrap)
|
||||||
|
|
|
@ -53,6 +53,9 @@ func (s *Server) setupSerf(conf *serf.Config, ch chan serf.Event, path string, w
|
||||||
conf.Tags["raft_vsn"] = fmt.Sprintf("%d", s.config.RaftConfig.ProtocolVersion)
|
conf.Tags["raft_vsn"] = fmt.Sprintf("%d", s.config.RaftConfig.ProtocolVersion)
|
||||||
conf.Tags["build"] = s.config.Build
|
conf.Tags["build"] = s.config.Build
|
||||||
addr := s.Listener.Addr().(*net.TCPAddr)
|
addr := s.Listener.Addr().(*net.TCPAddr)
|
||||||
|
if listener, ok := s.segmentListeners[segment]; ok {
|
||||||
|
addr = listener.Addr().(*net.TCPAddr)
|
||||||
|
}
|
||||||
conf.Tags["port"] = fmt.Sprintf("%d", addr.Port)
|
conf.Tags["port"] = fmt.Sprintf("%d", addr.Port)
|
||||||
if s.config.Bootstrap {
|
if s.config.Bootstrap {
|
||||||
conf.Tags["bootstrap"] = "1"
|
conf.Tags["bootstrap"] = "1"
|
||||||
|
|
|
@ -394,6 +394,16 @@ func (cmd *AgentCommand) readConfig() *agent.Config {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.Server && cfg.Segment != "" {
|
||||||
|
cmd.UI.Error("Segment option can only be set on clients")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfg.Server && len(cfg.Segments) > 0 {
|
||||||
|
cmd.UI.Error("Cannot define segments on clients")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// patch deprecated retry-join-{gce,azure,ec2)-* parameters
|
// patch deprecated retry-join-{gce,azure,ec2)-* parameters
|
||||||
// into -retry-join and issue warning.
|
// into -retry-join and issue warning.
|
||||||
// todo(fs): this should really be in DecodeConfig where it can be tested
|
// todo(fs): this should really be in DecodeConfig where it can be tested
|
||||||
|
|
Loading…
Reference in New Issue