consul/agent/metadata/server.go

234 lines
5.8 KiB
Go
Raw Normal View History

// Copyright (c) HashiCorp, Inc.
[COMPLIANCE] License changes (#18443) * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at <Blog URL>, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 * Update copyright file headers to BUSL-1.1 --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com>
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
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
ExternalGRPCTLSPort 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
}
}
var externalGRPCPort, externalGRPCTLSPort int
externalGRPCPortStr, foundGRPC := m.Tags["grpc_port"]
externalGRPCTLSPortStr, foundGRPCTLS := m.Tags["grpc_tls_port"]
if foundGRPC {
externalGRPCPort, _ = strconv.Atoi(externalGRPCPortStr)
}
if foundGRPCTLS {
externalGRPCTLSPort, _ = strconv.Atoi(externalGRPCTLSPortStr)
}
// If either port tag was found, check to ensure that at least one port was valid.
if foundGRPC || foundGRPCTLS {
if externalGRPCPort < 1 && externalGRPCTLSPort < 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,
ExternalGRPCTLSPort: externalGRPCTLSPort,
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"
}
}
func GetIP(addr net.Addr) []byte {
switch a := addr.(type) {
case *net.UDPAddr:
return []byte(a.IP.String())
case *net.TCPAddr:
return []byte(a.IP.String())
}
return []byte{}
}