2013-12-07 00:54:33 +00:00
|
|
|
package consul
|
|
|
|
|
|
|
|
import (
|
2014-04-04 23:34:23 +00:00
|
|
|
"crypto/tls"
|
2013-12-11 22:04:44 +00:00
|
|
|
"fmt"
|
2014-02-20 23:16:26 +00:00
|
|
|
"github.com/armon/go-metrics"
|
2013-12-19 20:03:57 +00:00
|
|
|
"github.com/hashicorp/consul/consul/structs"
|
2014-10-20 17:21:31 +00:00
|
|
|
"github.com/hashicorp/go-msgpack/codec"
|
2014-05-27 18:00:35 +00:00
|
|
|
"github.com/hashicorp/yamux"
|
2014-02-05 23:29:52 +00:00
|
|
|
"github.com/inconshreveable/muxado"
|
2013-12-11 22:57:40 +00:00
|
|
|
"io"
|
2013-12-12 00:33:19 +00:00
|
|
|
"math/rand"
|
2013-12-07 00:54:33 +00:00
|
|
|
"net"
|
2014-02-05 23:29:52 +00:00
|
|
|
"strings"
|
2014-02-05 18:38:29 +00:00
|
|
|
"time"
|
2013-12-07 00:54:33 +00:00
|
|
|
)
|
|
|
|
|
2013-12-09 21:13:40 +00:00
|
|
|
type RPCType byte
|
|
|
|
|
|
|
|
const (
|
|
|
|
rpcConsul RPCType = iota
|
|
|
|
rpcRaft
|
2014-02-05 23:29:52 +00:00
|
|
|
rpcMultiplex
|
2014-04-04 23:34:23 +00:00
|
|
|
rpcTLS
|
2014-05-27 18:00:35 +00:00
|
|
|
rpcMultiplexV2
|
2013-12-09 21:13:40 +00:00
|
|
|
)
|
|
|
|
|
2014-02-05 18:38:29 +00:00
|
|
|
const (
|
2014-05-10 02:16:40 +00:00
|
|
|
// maxQueryTime is used to bound the limit of a blocking query
|
2014-02-05 18:38:29 +00:00
|
|
|
maxQueryTime = 600 * time.Second
|
2014-04-29 05:25:09 +00:00
|
|
|
|
|
|
|
// Warn if the Raft command is larger than this.
|
2014-05-06 21:10:08 +00:00
|
|
|
// If it's over 1MB something is probably being abusive.
|
|
|
|
raftWarnSize = 1024 * 1024
|
2014-05-10 02:16:40 +00:00
|
|
|
|
|
|
|
// enqueueLimit caps how long we will wait to enqueue
|
|
|
|
// a new Raft command. Something is probably wrong if this
|
|
|
|
// value is ever reached. However, it prevents us from blocking
|
|
|
|
// the requesting goroutine forever.
|
|
|
|
enqueueLimit = 30 * time.Second
|
2014-02-05 18:38:29 +00:00
|
|
|
)
|
|
|
|
|
2013-12-07 00:54:33 +00:00
|
|
|
// listen is used to listen for incoming RPC connections
|
|
|
|
func (s *Server) listen() {
|
|
|
|
for {
|
|
|
|
// Accept a connection
|
|
|
|
conn, err := s.rpcListener.Accept()
|
|
|
|
if err != nil {
|
|
|
|
if s.shutdown {
|
|
|
|
return
|
|
|
|
}
|
2014-01-10 19:06:11 +00:00
|
|
|
s.logger.Printf("[ERR] consul.rpc: failed to accept RPC conn: %v", err)
|
2013-12-07 00:54:33 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2014-04-07 19:45:33 +00:00
|
|
|
go s.handleConn(conn, false)
|
2014-02-20 23:16:26 +00:00
|
|
|
metrics.IncrCounter([]string{"consul", "rpc", "accept_conn"}, 1)
|
2013-12-07 00:54:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-09 21:13:40 +00:00
|
|
|
// handleConn is used to determine if this is a Raft or
|
|
|
|
// Consul type RPC connection and invoke the correct handler
|
2014-04-07 19:45:33 +00:00
|
|
|
func (s *Server) handleConn(conn net.Conn, isTLS bool) {
|
2013-12-09 21:13:40 +00:00
|
|
|
// Read a single byte
|
|
|
|
buf := make([]byte, 1)
|
|
|
|
if _, err := conn.Read(buf); err != nil {
|
2014-05-23 23:28:55 +00:00
|
|
|
if err != io.EOF {
|
|
|
|
s.logger.Printf("[ERR] consul.rpc: failed to read byte: %v", err)
|
|
|
|
}
|
2013-12-09 21:13:40 +00:00
|
|
|
conn.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-04-04 23:43:00 +00:00
|
|
|
// Enforce TLS if VerifyIncoming is set
|
2014-04-07 19:45:33 +00:00
|
|
|
if s.config.VerifyIncoming && !isTLS && RPCType(buf[0]) != rpcTLS {
|
2014-04-04 23:43:00 +00:00
|
|
|
s.logger.Printf("[WARN] consul.rpc: Non-TLS connection attempted with VerifyIncoming set")
|
|
|
|
conn.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-12-09 21:13:40 +00:00
|
|
|
// Switch on the byte
|
|
|
|
switch RPCType(buf[0]) {
|
|
|
|
case rpcConsul:
|
|
|
|
s.handleConsulConn(conn)
|
|
|
|
|
|
|
|
case rpcRaft:
|
2014-02-20 23:16:26 +00:00
|
|
|
metrics.IncrCounter([]string{"consul", "rpc", "raft_handoff"}, 1)
|
2013-12-09 21:13:40 +00:00
|
|
|
s.raftLayer.Handoff(conn)
|
|
|
|
|
2014-02-05 23:29:52 +00:00
|
|
|
case rpcMultiplex:
|
|
|
|
s.handleMultiplex(conn)
|
|
|
|
|
2014-04-07 19:45:33 +00:00
|
|
|
case rpcTLS:
|
|
|
|
if s.rpcTLS == nil {
|
|
|
|
s.logger.Printf("[WARN] consul.rpc: TLS connection attempted, server not configured for TLS")
|
|
|
|
conn.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
conn = tls.Server(conn, s.rpcTLS)
|
|
|
|
s.handleConn(conn, true)
|
|
|
|
|
2014-05-27 18:00:35 +00:00
|
|
|
case rpcMultiplexV2:
|
|
|
|
s.handleMultiplexV2(conn)
|
|
|
|
|
2013-12-09 21:13:40 +00:00
|
|
|
default:
|
2014-01-10 19:06:11 +00:00
|
|
|
s.logger.Printf("[ERR] consul.rpc: unrecognized RPC byte: %v", buf[0])
|
2013-12-09 21:13:40 +00:00
|
|
|
conn.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-05 23:29:52 +00:00
|
|
|
// handleMultiplex is used to multiplex a single incoming connection
|
2014-05-27 18:00:35 +00:00
|
|
|
// using the Muxado multiplexer
|
2014-02-05 23:29:52 +00:00
|
|
|
func (s *Server) handleMultiplex(conn net.Conn) {
|
|
|
|
defer conn.Close()
|
|
|
|
server := muxado.Server(conn)
|
|
|
|
for {
|
|
|
|
sub, err := server.Accept()
|
|
|
|
if err != nil {
|
|
|
|
if !strings.Contains(err.Error(), "closed") {
|
|
|
|
s.logger.Printf("[ERR] consul.rpc: multiplex conn accept failed: %v", err)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
go s.handleConsulConn(sub)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-27 18:00:35 +00:00
|
|
|
// handleMultiplexV2 is used to multiplex a single incoming connection
|
|
|
|
// using the Yamux multiplexer
|
|
|
|
func (s *Server) handleMultiplexV2(conn net.Conn) {
|
|
|
|
defer conn.Close()
|
2014-05-28 23:32:10 +00:00
|
|
|
conf := yamux.DefaultConfig()
|
|
|
|
conf.LogOutput = s.config.LogOutput
|
|
|
|
server, _ := yamux.Server(conn, conf)
|
2014-05-27 18:00:35 +00:00
|
|
|
for {
|
|
|
|
sub, err := server.Accept()
|
|
|
|
if err != nil {
|
|
|
|
if err != io.EOF {
|
|
|
|
s.logger.Printf("[ERR] consul.rpc: multiplex conn accept failed: %v", err)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
go s.handleConsulConn(sub)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-09 21:13:40 +00:00
|
|
|
// handleConsulConn is used to service a single Consul RPC connection
|
|
|
|
func (s *Server) handleConsulConn(conn net.Conn) {
|
2014-05-15 00:34:24 +00:00
|
|
|
defer conn.Close()
|
2014-06-08 21:02:42 +00:00
|
|
|
rpcCodec := codec.GoRpc.ServerCodec(conn, msgpackHandle)
|
2013-12-07 00:54:33 +00:00
|
|
|
for !s.shutdown {
|
|
|
|
if err := s.rpcServer.ServeRequest(rpcCodec); err != nil {
|
2014-05-28 23:28:06 +00:00
|
|
|
if err != io.EOF && !strings.Contains(err.Error(), "closed") {
|
2014-01-10 19:06:11 +00:00
|
|
|
s.logger.Printf("[ERR] consul.rpc: RPC error: %v (%v)", err, conn)
|
2013-12-11 22:57:40 +00:00
|
|
|
}
|
2013-12-07 00:54:33 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-11 22:04:44 +00:00
|
|
|
|
|
|
|
// forward is used to forward to a remote DC or to forward to the local leader
|
|
|
|
// Returns a bool of if forwarding was performed, as well as any error
|
2014-04-19 00:17:12 +00:00
|
|
|
func (s *Server) forward(method string, info structs.RPCInfo, args interface{}, reply interface{}) (bool, error) {
|
2013-12-11 22:04:44 +00:00
|
|
|
// Handle DC forwarding
|
2014-04-19 00:17:12 +00:00
|
|
|
dc := info.RequestDatacenter()
|
2013-12-11 22:04:44 +00:00
|
|
|
if dc != s.config.Datacenter {
|
|
|
|
err := s.forwardDC(method, dc, args, reply)
|
|
|
|
return true, err
|
|
|
|
}
|
|
|
|
|
2014-04-19 00:26:59 +00:00
|
|
|
// Check if we can allow a stale read
|
|
|
|
if info.IsRead() && info.AllowStaleRead() {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
2013-12-11 22:04:44 +00:00
|
|
|
// Handle leader forwarding
|
|
|
|
if !s.IsLeader() {
|
|
|
|
err := s.forwardLeader(method, args, reply)
|
|
|
|
return true, err
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// forwardLeader is used to forward an RPC call to the leader, or fail if no leader
|
|
|
|
func (s *Server) forwardLeader(method string, args interface{}, reply interface{}) error {
|
2014-05-27 22:45:19 +00:00
|
|
|
// Get the leader
|
2013-12-11 22:04:44 +00:00
|
|
|
leader := s.raft.Leader()
|
|
|
|
if leader == nil {
|
2013-12-19 20:03:57 +00:00
|
|
|
return structs.ErrNoLeader
|
2013-12-11 22:04:44 +00:00
|
|
|
}
|
2014-05-27 22:45:19 +00:00
|
|
|
|
|
|
|
// Lookup the server
|
|
|
|
s.localLock.RLock()
|
|
|
|
server := s.localConsuls[leader.String()]
|
|
|
|
s.localLock.RUnlock()
|
|
|
|
|
|
|
|
// Handle a missing server
|
|
|
|
if server == nil {
|
|
|
|
return structs.ErrNoLeader
|
|
|
|
}
|
|
|
|
return s.connPool.RPC(server.Addr, server.Version, method, args, reply)
|
2013-12-11 22:04:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// forwardDC is used to forward an RPC call to a remote DC, or fail if no servers
|
|
|
|
func (s *Server) forwardDC(method, dc string, args interface{}, reply interface{}) error {
|
2013-12-12 00:33:19 +00:00
|
|
|
// Bail if we can't find any servers
|
|
|
|
s.remoteLock.RLock()
|
|
|
|
servers := s.remoteConsuls[dc]
|
|
|
|
if len(servers) == 0 {
|
|
|
|
s.remoteLock.RUnlock()
|
2014-01-10 19:06:11 +00:00
|
|
|
s.logger.Printf("[WARN] consul.rpc: RPC request for DC '%s', no path found", dc)
|
2013-12-19 20:03:57 +00:00
|
|
|
return structs.ErrNoDCPath
|
2013-12-12 00:33:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Select a random addr
|
|
|
|
offset := rand.Int31() % int32(len(servers))
|
|
|
|
server := servers[offset]
|
|
|
|
s.remoteLock.RUnlock()
|
|
|
|
|
|
|
|
// Forward to remote Consul
|
2014-02-20 23:16:26 +00:00
|
|
|
metrics.IncrCounter([]string{"consul", "rpc", "cross-dc", dc}, 1)
|
2014-05-27 22:07:31 +00:00
|
|
|
return s.connPool.RPC(server.Addr, server.Version, method, args, reply)
|
2013-12-11 22:04:44 +00:00
|
|
|
}
|
|
|
|
|
2014-10-02 06:09:00 +00:00
|
|
|
// globalRPC is used to forward an RPC request to one server in each datacenter.
|
|
|
|
// This will only error for RPC-related errors. Otherwise, application-level
|
2014-10-05 20:15:59 +00:00
|
|
|
// errors can be sent in the response objects.
|
2014-10-02 06:09:00 +00:00
|
|
|
func (s *Server) globalRPC(method string, args interface{},
|
|
|
|
reply structs.CompoundResponse) error {
|
|
|
|
|
|
|
|
errorCh := make(chan error)
|
|
|
|
respCh := make(chan interface{})
|
|
|
|
|
|
|
|
// Make a new request into each datacenter
|
|
|
|
for dc, _ := range s.remoteConsuls {
|
2014-10-08 20:28:59 +00:00
|
|
|
go func(dc string) {
|
2014-10-02 06:09:00 +00:00
|
|
|
rr := reply.New()
|
2014-10-08 20:28:59 +00:00
|
|
|
if err := s.forwardDC(method, dc, args, &rr); err != nil {
|
2014-10-02 06:09:00 +00:00
|
|
|
errorCh <- err
|
|
|
|
return
|
|
|
|
}
|
|
|
|
respCh <- rr
|
2014-10-08 20:28:59 +00:00
|
|
|
}(dc)
|
2014-10-02 06:09:00 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 20:28:59 +00:00
|
|
|
replies, total := 0, len(s.remoteConsuls)
|
|
|
|
for replies < total {
|
2014-10-02 06:09:00 +00:00
|
|
|
select {
|
|
|
|
case err := <-errorCh:
|
|
|
|
return err
|
|
|
|
case rr := <-respCh:
|
|
|
|
reply.Add(rr)
|
2014-10-06 22:14:30 +00:00
|
|
|
replies++
|
2014-10-02 06:09:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2013-12-11 22:04:44 +00:00
|
|
|
// raftApply is used to encode a message, run it through raft, and return
|
|
|
|
// the FSM response along with any errors
|
2013-12-19 20:03:57 +00:00
|
|
|
func (s *Server) raftApply(t structs.MessageType, msg interface{}) (interface{}, error) {
|
|
|
|
buf, err := structs.Encode(t, msg)
|
2013-12-11 22:04:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to encode request: %v", err)
|
|
|
|
}
|
|
|
|
|
2014-04-29 05:25:09 +00:00
|
|
|
// Warn if the command is very large
|
|
|
|
if n := len(buf); n > raftWarnSize {
|
|
|
|
s.logger.Printf("[WARN] consul: Attempting to apply large raft entry (%d bytes)", n)
|
|
|
|
}
|
|
|
|
|
2014-05-10 02:16:40 +00:00
|
|
|
future := s.raft.Apply(buf, enqueueLimit)
|
2013-12-11 22:04:44 +00:00
|
|
|
if err := future.Error(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return future.Response(), nil
|
|
|
|
}
|
2014-02-05 18:38:29 +00:00
|
|
|
|
|
|
|
// blockingRPC is used for queries that need to wait for a
|
|
|
|
// minimum index. This is used to block and wait for changes.
|
2014-04-21 18:31:15 +00:00
|
|
|
func (s *Server) blockingRPC(b *structs.QueryOptions, m *structs.QueryMeta,
|
2014-04-21 18:18:27 +00:00
|
|
|
tables MDBTables, run func() error) error {
|
2014-02-05 18:38:29 +00:00
|
|
|
var timeout <-chan time.Time
|
|
|
|
var notifyCh chan struct{}
|
|
|
|
|
|
|
|
// Fast path non-blocking
|
|
|
|
if b.MinQueryIndex == 0 {
|
|
|
|
goto RUN_QUERY
|
|
|
|
}
|
|
|
|
|
2014-02-05 19:13:08 +00:00
|
|
|
// Sanity check that we have tables to block on
|
|
|
|
if len(tables) == 0 {
|
|
|
|
panic("no tables to block on")
|
|
|
|
}
|
|
|
|
|
2014-02-05 18:38:29 +00:00
|
|
|
// Restrict the max query time
|
|
|
|
if b.MaxQueryTime > maxQueryTime {
|
|
|
|
b.MaxQueryTime = maxQueryTime
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure a time limit is set if we have an index
|
|
|
|
if b.MinQueryIndex > 0 && b.MaxQueryTime == 0 {
|
|
|
|
b.MaxQueryTime = maxQueryTime
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup a query timeout
|
|
|
|
if b.MaxQueryTime > 0 {
|
|
|
|
timeout = time.After(b.MaxQueryTime)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup a notification channel for changes
|
|
|
|
SETUP_NOTIFY:
|
|
|
|
if b.MinQueryIndex > 0 {
|
|
|
|
notifyCh = make(chan struct{}, 1)
|
|
|
|
s.fsm.State().Watch(tables, notifyCh)
|
|
|
|
}
|
|
|
|
|
|
|
|
RUN_QUERY:
|
2014-04-21 18:04:52 +00:00
|
|
|
// Update the query meta data
|
|
|
|
s.setQueryMeta(m)
|
|
|
|
|
2014-04-21 18:49:21 +00:00
|
|
|
// Check if query must be consistent
|
|
|
|
if b.RequireConsistent {
|
|
|
|
if err := s.consistentRead(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-21 18:04:52 +00:00
|
|
|
// Run the query function
|
2014-04-21 18:18:27 +00:00
|
|
|
err := run()
|
2014-02-05 18:38:29 +00:00
|
|
|
|
|
|
|
// Check for minimum query time
|
2014-04-21 18:18:27 +00:00
|
|
|
if err == nil && m.Index > 0 && m.Index <= b.MinQueryIndex {
|
2014-02-05 18:38:29 +00:00
|
|
|
select {
|
|
|
|
case <-notifyCh:
|
|
|
|
goto SETUP_NOTIFY
|
|
|
|
case <-timeout:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
2014-04-19 00:37:19 +00:00
|
|
|
|
|
|
|
// setQueryMeta is used to populate the QueryMeta data for an RPC call
|
|
|
|
func (s *Server) setQueryMeta(m *structs.QueryMeta) {
|
|
|
|
if s.IsLeader() {
|
|
|
|
m.LastContact = 0
|
|
|
|
m.KnownLeader = true
|
|
|
|
} else {
|
|
|
|
m.LastContact = time.Now().Sub(s.raft.LastContact())
|
|
|
|
m.KnownLeader = (s.raft.Leader() != nil)
|
|
|
|
}
|
|
|
|
}
|
2014-04-19 00:49:01 +00:00
|
|
|
|
|
|
|
// consistentRead is used to ensure we do not perform a stale
|
|
|
|
// read. This is done by verifying leadership before the read.
|
|
|
|
func (s *Server) consistentRead() error {
|
|
|
|
defer metrics.MeasureSince([]string{"consul", "rpc", "consistentRead"}, time.Now())
|
|
|
|
future := s.raft.VerifyLeader()
|
|
|
|
return future.Error()
|
|
|
|
}
|