consul/agent/metadata/server.go

215 lines
5.1 KiB
Go
Raw Normal View History

package metadata
import (
"fmt"
"net"
2017-03-21 23:36:44 +00:00
"regexp"
"strconv"
"strings"
2017-03-21 23:36:44 +00:00
"github.com/hashicorp/go-version"
"github.com/hashicorp/serf/serf"
)
// Key is used in maps and for equality tests. A key is based on endpoints.
type Key struct {
name string
}
// Equal compares two Key objects
func (k *Key) Equal(x *Key) bool {
return k.name == x.name
}
2016-03-30 00:39:19 +00:00
// Server is used to return details of a consul server
type Server struct {
Name string // <node>.<dc>
ShortName string // <node>
ID string
Datacenter string
Segment string
Port int
SegmentAddrs map[string]string
SegmentPorts map[string]int
WanJoinPort int
LanJoinPort int
ExternalGRPCPort int
Bootstrap bool
Expect int
Build version.Version
Version int
RaftVersion int
Addr net.Addr
Status serf.MemberStatus
ReadReplica bool
FeatureFlags map[string]int
// If true, use TLS when connecting to this server
UseTLS bool
}
// Key returns the corresponding Key
2016-03-30 00:39:19 +00:00
func (s *Server) Key() *Key {
return &Key{
name: s.Name,
}
}
2016-03-30 00:39:19 +00:00
// String returns a string representation of Server
func (s *Server) String() string {
var addrStr, networkStr string
if s.Addr != nil {
addrStr = s.Addr.String()
networkStr = s.Addr.Network()
}
return fmt.Sprintf("%s (Addr: %s/%s) (DC: %s)", s.Name, networkStr, addrStr, s.Datacenter)
}
2017-03-21 23:36:44 +00:00
var versionFormat = regexp.MustCompile(`\d+\.\d+\.\d+`)
2016-03-30 00:39:19 +00:00
// IsConsulServer returns true if a serf member is a consul server
// agent. Returns a bool and a pointer to the Server.
func IsConsulServer(m serf.Member) (bool, *Server) {
if m.Tags["role"] != "consul" {
return false, nil
}
datacenter := m.Tags["dc"]
segment := m.Tags["segment"]
_, bootstrap := m.Tags["bootstrap"]
_, useTLS := m.Tags["use_tls"]
expect := 0
2018-01-28 18:40:13 +00:00
expectStr, ok := m.Tags["expect"]
var err error
if ok {
2018-01-28 18:40:13 +00:00
expect, err = strconv.Atoi(expectStr)
if err != nil {
return false, nil
}
}
2018-01-28 18:40:13 +00:00
portStr := m.Tags["port"]
port, err := strconv.Atoi(portStr)
if err != nil {
return false, nil
}
2018-01-28 18:40:13 +00:00
segmentAddrs := make(map[string]string)
segmentPorts := make(map[string]int)
featureFlags := make(map[string]int)
for name, value := range m.Tags {
if strings.HasPrefix(name, "sl_") {
addr, port, err := net.SplitHostPort(value)
if err != nil {
return false, nil
}
2018-01-28 18:40:13 +00:00
segmentPort, err := strconv.Atoi(port)
if err != nil {
return false, nil
}
2018-01-28 18:40:13 +00:00
segmentName := strings.TrimPrefix(name, "sl_")
segmentAddrs[segmentName] = addr
segmentPorts[segmentName] = segmentPort
} else if strings.HasPrefix(name, featureFlagPrefix) {
featureName := strings.TrimPrefix(name, featureFlagPrefix)
featureState, err := strconv.Atoi(value)
if err != nil {
return false, nil
}
featureFlags[featureName] = featureState
}
}
2018-01-28 18:40:13 +00:00
buildVersion, err := Build(&m)
2017-03-21 23:36:44 +00:00
if err != nil {
return false, nil
}
2018-01-28 18:40:13 +00:00
wanJoinPort := 0
wanJoinPortStr, ok := m.Tags["wan_join_port"]
2017-03-15 19:26:54 +00:00
if ok {
2018-01-28 18:40:13 +00:00
wanJoinPort, err = strconv.Atoi(wanJoinPortStr)
2017-03-15 19:26:54 +00:00
if err != nil {
return false, nil
}
}
externalGRPCPort := 0
externalGRPCPortStr, ok := m.Tags["grpc_port"]
if ok {
externalGRPCPort, err = strconv.Atoi(externalGRPCPortStr)
if err != nil {
return false, nil
}
if externalGRPCPort < 1 {
return false, nil
}
}
2018-01-28 18:40:13 +00:00
vsnStr := m.Tags["vsn"]
vsn, err := strconv.Atoi(vsnStr)
if err != nil {
return false, nil
}
2018-01-28 18:40:13 +00:00
raftVsn := 0
2018-01-28 18:48:21 +00:00
raftVsnStr, ok := m.Tags["raft_vsn"]
if ok {
2018-01-28 18:48:21 +00:00
raftVsn, err = strconv.Atoi(raftVsnStr)
if err != nil {
return false, nil
}
2017-02-22 20:53:32 +00:00
}
2018-09-20 00:41:36 +00:00
// Check if the server is a non voter
// DEPRECATED - remove looking for the nonvoter tag eventually once we don't have to support
// read replicas running v1.8.x and below.
2018-09-20 00:41:36 +00:00
_, nonVoter := m.Tags["nonvoter"]
_, readReplica := m.Tags["read_replica"]
2018-09-20 00:41:36 +00:00
addr := &net.TCPAddr{IP: m.Addr, Port: port}
2016-03-30 00:39:19 +00:00
parts := &Server{
Name: m.Name,
ShortName: strings.TrimSuffix(m.Name, "."+datacenter),
ID: m.Tags["id"],
Datacenter: datacenter,
Segment: segment,
Port: port,
SegmentAddrs: segmentAddrs,
SegmentPorts: segmentPorts,
WanJoinPort: wanJoinPort,
LanJoinPort: int(m.Port),
ExternalGRPCPort: externalGRPCPort,
Bootstrap: bootstrap,
Expect: expect,
Addr: addr,
Build: *buildVersion,
Version: vsn,
RaftVersion: raftVsn,
Status: m.Status,
UseTLS: useTLS,
// DEPRECATED - remove nonVoter check once support for that tag is removed
ReadReplica: nonVoter || readReplica,
FeatureFlags: featureFlags,
}
return true, parts
}
// TODO(ACL-Legacy-Compat): remove in phase 2
const TagACLs = "acls"
const featureFlagPrefix = "ft_"
// AddFeatureFlags to the tags. The tags map is expected to be a serf.Config.Tags.
// The feature flags are encoded in the tags so that IsConsulServer can decode them
// and populate the Server.FeatureFlags map.
func AddFeatureFlags(tags map[string]string, flags ...string) {
for _, flag := range flags {
tags[featureFlagPrefix+flag] = "1"
}
}