client/fingerprint/consul: refactor the consul fingerprinter to test individual attributes
This PR refactors the ConsulFingerprint implementation, breaking individual attributes into individual functions to make testing them easier. This is in preparation for additional extractors about to be added. Behavior should be otherwise unchanged. It adds the attribute consul.sku, which can be used to differentiate between Consul OSS vs Consul ENT.
This commit is contained in:
parent
e9777a88ce
commit
b548cf6816
|
@ -3,10 +3,12 @@ package fingerprint
|
|||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
consul "github.com/hashicorp/consul/api"
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -16,78 +18,120 @@ const (
|
|||
|
||||
// ConsulFingerprint is used to fingerprint for Consul
|
||||
type ConsulFingerprint struct {
|
||||
logger log.Logger
|
||||
client *consul.Client
|
||||
lastState string
|
||||
logger log.Logger
|
||||
client *consul.Client
|
||||
lastState string
|
||||
extractors map[string]consulExtractor
|
||||
}
|
||||
|
||||
// consulInfo aliases the type returned from the Consul agent self endpoint.
|
||||
type consulInfo = map[string]map[string]interface{}
|
||||
|
||||
// consulExtractor is used to parse out one attribute from consulInfo. Returns
|
||||
// the value of the attribute, and whether the attribute exists.
|
||||
type consulExtractor func(consulInfo) (string, bool)
|
||||
|
||||
// NewConsulFingerprint is used to create a Consul fingerprint
|
||||
func NewConsulFingerprint(logger log.Logger) Fingerprint {
|
||||
return &ConsulFingerprint{logger: logger.Named("consul"), lastState: consulUnavailable}
|
||||
return &ConsulFingerprint{
|
||||
logger: logger.Named("consul"),
|
||||
lastState: consulUnavailable,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *ConsulFingerprint) Fingerprint(req *FingerprintRequest, resp *FingerprintResponse) error {
|
||||
// Only create the client once to avoid creating too many connections to
|
||||
// Consul.
|
||||
|
||||
// establish consul client if necessary
|
||||
if err := f.initialize(req); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// query consul for agent self api
|
||||
info := f.query(resp)
|
||||
if len(info) == 0 {
|
||||
// unable to reach consul, nothing to do this time
|
||||
return nil
|
||||
}
|
||||
|
||||
// apply the extractor for each attribute
|
||||
for attr, extractor := range f.extractors {
|
||||
if s, ok := extractor(info); !ok {
|
||||
f.logger.Warn("unable to fingerprint consul", "attribute", attr)
|
||||
} else {
|
||||
resp.AddAttribute(attr, s)
|
||||
}
|
||||
}
|
||||
|
||||
// create link for consul
|
||||
f.link(resp)
|
||||
|
||||
// indicate Consul is now available
|
||||
if f.lastState == consulUnavailable {
|
||||
f.logger.Info("consul agent is available")
|
||||
}
|
||||
|
||||
f.lastState = consulAvailable
|
||||
resp.Detected = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *ConsulFingerprint) Periodic() (bool, time.Duration) {
|
||||
return true, 15 * time.Second
|
||||
}
|
||||
|
||||
// clearConsulAttributes removes consul attributes and links from the passed Node.
|
||||
func (f *ConsulFingerprint) clearConsulAttributes(r *FingerprintResponse) {
|
||||
for attr := range f.extractors {
|
||||
r.RemoveAttribute(attr)
|
||||
}
|
||||
r.RemoveLink("consul")
|
||||
}
|
||||
|
||||
func (f *ConsulFingerprint) initialize(req *FingerprintRequest) error {
|
||||
// Only create the Consul client once to avoid creating many connections
|
||||
if f.client == nil {
|
||||
consulConfig, err := req.Config.ConsulConfig.ApiConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to initialize the Consul client config: %v", err)
|
||||
return fmt.Errorf("failed to initialize Consul client config: %v", err)
|
||||
}
|
||||
|
||||
f.client, err = consul.NewClient(consulConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to initialize consul client: %s", err)
|
||||
return fmt.Errorf("failed to initialize Consul client: %s", err)
|
||||
}
|
||||
|
||||
f.extractors = map[string]consulExtractor{
|
||||
"consul.server": f.server,
|
||||
"consul.version": f.version,
|
||||
"consul.sku": f.sku,
|
||||
"consul.revision": f.revision,
|
||||
"unique.consul.name": f.name,
|
||||
"consul.datacenter": f.dc,
|
||||
"consul.segment": f.segment,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *ConsulFingerprint) query(resp *FingerprintResponse) consulInfo {
|
||||
// We'll try to detect consul by making a query to to the agent's self API.
|
||||
// If we can't hit this URL consul is probably not running on this machine.
|
||||
info, err := f.client.Agent().Self()
|
||||
if err != nil {
|
||||
f.clearConsulAttributes(resp)
|
||||
|
||||
// Print a message indicating that the Consul Agent is not available
|
||||
// anymore
|
||||
// indicate consul no longer available
|
||||
if f.lastState == consulAvailable {
|
||||
f.logger.Info("consul agent is unavailable")
|
||||
}
|
||||
f.lastState = consulUnavailable
|
||||
return nil
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
if s, ok := info["Config"]["Server"].(bool); ok {
|
||||
resp.AddAttribute("consul.server", strconv.FormatBool(s))
|
||||
} else {
|
||||
f.logger.Warn("unable to fingerprint consul.server")
|
||||
}
|
||||
if v, ok := info["Config"]["Version"].(string); ok {
|
||||
resp.AddAttribute("consul.version", v)
|
||||
} else {
|
||||
f.logger.Warn("unable to fingerprint consul.version")
|
||||
}
|
||||
if r, ok := info["Config"]["Revision"].(string); ok {
|
||||
resp.AddAttribute("consul.revision", r)
|
||||
} else {
|
||||
f.logger.Warn("unable to fingerprint consul.revision")
|
||||
}
|
||||
if n, ok := info["Config"]["NodeName"].(string); ok {
|
||||
resp.AddAttribute("unique.consul.name", n)
|
||||
} else {
|
||||
f.logger.Warn("unable to fingerprint unique.consul.name")
|
||||
}
|
||||
if d, ok := info["Config"]["Datacenter"].(string); ok {
|
||||
resp.AddAttribute("consul.datacenter", d)
|
||||
} else {
|
||||
f.logger.Warn("unable to fingerprint consul.datacenter")
|
||||
}
|
||||
if g, ok := info["Member"]["Tags"].(map[string]interface{}); ok {
|
||||
if s, ok := g["segment"].(string); ok {
|
||||
resp.AddAttribute("consul.segment", s)
|
||||
}
|
||||
} else {
|
||||
f.logger.Warn("unable to fingerprint consul.segment")
|
||||
}
|
||||
func (f *ConsulFingerprint) link(resp *FingerprintResponse) {
|
||||
if dc, ok := resp.Attributes["consul.datacenter"]; ok {
|
||||
if name, ok2 := resp.Attributes["unique.consul.name"]; ok2 {
|
||||
resp.AddLink("consul", fmt.Sprintf("%s.%s", dc, name))
|
||||
|
@ -95,29 +139,54 @@ func (f *ConsulFingerprint) Fingerprint(req *FingerprintRequest, resp *Fingerpri
|
|||
} else {
|
||||
f.logger.Warn("malformed Consul response prevented linking")
|
||||
}
|
||||
}
|
||||
|
||||
// If the Consul Agent was previously unavailable print a message to
|
||||
// indicate the Agent is available now
|
||||
if f.lastState == consulUnavailable {
|
||||
f.logger.Info("consul agent is available")
|
||||
func (f *ConsulFingerprint) server(info consulInfo) (string, bool) {
|
||||
s, ok := info["Config"]["Server"].(bool)
|
||||
return strconv.FormatBool(s), ok
|
||||
}
|
||||
|
||||
func (f *ConsulFingerprint) version(info consulInfo) (string, bool) {
|
||||
v, ok := info["Config"]["Version"].(string)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (f *ConsulFingerprint) sku(info consulInfo) (string, bool) {
|
||||
v, ok := info["Config"]["Version"].(string)
|
||||
if !ok {
|
||||
return "", ok
|
||||
}
|
||||
f.lastState = consulAvailable
|
||||
resp.Detected = true
|
||||
return nil
|
||||
|
||||
ver, vErr := version.NewVersion(v)
|
||||
if vErr != nil {
|
||||
return "", false
|
||||
}
|
||||
if strings.Contains(ver.Metadata(), "ent") {
|
||||
return "ent", true
|
||||
}
|
||||
return "oss", true
|
||||
}
|
||||
|
||||
// clearConsulAttributes removes consul attributes and links from the passed
|
||||
// Node.
|
||||
func (f *ConsulFingerprint) clearConsulAttributes(r *FingerprintResponse) {
|
||||
r.RemoveAttribute("consul.server")
|
||||
r.RemoveAttribute("consul.version")
|
||||
r.RemoveAttribute("consul.revision")
|
||||
r.RemoveAttribute("unique.consul.name")
|
||||
r.RemoveAttribute("consul.datacenter")
|
||||
r.RemoveAttribute("consul.segment")
|
||||
r.RemoveLink("consul")
|
||||
func (f *ConsulFingerprint) revision(info consulInfo) (string, bool) {
|
||||
r, ok := info["Config"]["Revision"].(string)
|
||||
return r, ok
|
||||
}
|
||||
|
||||
func (f *ConsulFingerprint) Periodic() (bool, time.Duration) {
|
||||
return true, 15 * time.Second
|
||||
func (f *ConsulFingerprint) name(info consulInfo) (string, bool) {
|
||||
n, ok := info["Config"]["NodeName"].(string)
|
||||
return n, ok
|
||||
}
|
||||
|
||||
func (f *ConsulFingerprint) dc(info consulInfo) (string, bool) {
|
||||
d, ok := info["Config"]["Datacenter"].(string)
|
||||
return d, ok
|
||||
}
|
||||
|
||||
func (f *ConsulFingerprint) segment(info consulInfo) (string, bool) {
|
||||
tags, tagsOK := info["Member"]["Tags"].(map[string]interface{})
|
||||
if !tagsOK {
|
||||
return "", false
|
||||
}
|
||||
s, ok := tags["segment"].(string)
|
||||
return s, ok
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package fingerprint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
@ -10,207 +11,327 @@ import (
|
|||
"github.com/hashicorp/nomad/client/config"
|
||||
"github.com/hashicorp/nomad/helper/testlog"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConsulFingerprint(t *testing.T) {
|
||||
fp := NewConsulFingerprint(testlog.HCLogger(t))
|
||||
node := &structs.Node{
|
||||
Attributes: make(map[string]string),
|
||||
}
|
||||
// fakeConsul creates an HTTP server mimicking Consul /v1/agent/self endpoint on
|
||||
// the first request, and alternates between success and failure responses on
|
||||
// subsequent requests
|
||||
func fakeConsul(payload string) (*httptest.Server, *config.Config) {
|
||||
working := true
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintln(w, mockConsulResponse)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
conf := config.DefaultConfig()
|
||||
conf.ConsulConfig.Addr = strings.TrimPrefix(ts.URL, "http://")
|
||||
|
||||
request := &FingerprintRequest{Config: conf, Node: node}
|
||||
var response FingerprintResponse
|
||||
err := fp.Fingerprint(request, &response)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to fingerprint: %s", err)
|
||||
}
|
||||
|
||||
if !response.Detected {
|
||||
t.Fatalf("expected response to be applicable")
|
||||
}
|
||||
|
||||
assertNodeAttributeContains(t, response.Attributes, "consul.server")
|
||||
assertNodeAttributeContains(t, response.Attributes, "consul.version")
|
||||
assertNodeAttributeContains(t, response.Attributes, "consul.revision")
|
||||
assertNodeAttributeContains(t, response.Attributes, "unique.consul.name")
|
||||
assertNodeAttributeContains(t, response.Attributes, "consul.datacenter")
|
||||
assertNodeAttributeContains(t, response.Attributes, "consul.segment")
|
||||
|
||||
if _, ok := response.Links["consul"]; !ok {
|
||||
t.Errorf("Expected a link to consul, none found")
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from tryconsul using consul release 0.5.2
|
||||
const mockConsulResponse = `
|
||||
{
|
||||
"Config": {
|
||||
"Bootstrap": false,
|
||||
"BootstrapExpect": 3,
|
||||
"Server": true,
|
||||
"Datacenter": "vagrant",
|
||||
"DataDir": "/var/lib/consul",
|
||||
"DNSRecursor": "",
|
||||
"DNSRecursors": [],
|
||||
"DNSConfig": {
|
||||
"NodeTTL": 0,
|
||||
"ServiceTTL": null,
|
||||
"AllowStale": false,
|
||||
"EnableTruncate": false,
|
||||
"MaxStale": 5000000000,
|
||||
"OnlyPassing": false
|
||||
},
|
||||
"Domain": "consul.",
|
||||
"LogLevel": "INFO",
|
||||
"NodeName": "consul2",
|
||||
"ClientAddr": "0.0.0.0",
|
||||
"BindAddr": "0.0.0.0",
|
||||
"AdvertiseAddr": "172.16.59.133",
|
||||
"AdvertiseAddrWan": "172.16.59.133",
|
||||
"Ports": {
|
||||
"DNS": 8600,
|
||||
"HTTP": 8500,
|
||||
"HTTPS": -1,
|
||||
"RPC": 8400,
|
||||
"SerfLan": 8301,
|
||||
"SerfWan": 8302,
|
||||
"Server": 8300
|
||||
},
|
||||
"Addresses": {
|
||||
"DNS": "",
|
||||
"HTTP": "",
|
||||
"HTTPS": "",
|
||||
"RPC": ""
|
||||
},
|
||||
"LeaveOnTerm": false,
|
||||
"SkipLeaveOnInt": false,
|
||||
"StatsiteAddr": "",
|
||||
"StatsitePrefix": "consul",
|
||||
"StatsdAddr": "",
|
||||
"Protocol": 2,
|
||||
"EnableDebug": false,
|
||||
"VerifyIncoming": false,
|
||||
"VerifyOutgoing": false,
|
||||
"VerifyServerHostname": false,
|
||||
"CAFile": "",
|
||||
"CertFile": "",
|
||||
"KeyFile": "",
|
||||
"ServerName": "",
|
||||
"StartJoin": [],
|
||||
"StartJoinWan": [],
|
||||
"RetryJoin": [],
|
||||
"RetryMaxAttempts": 0,
|
||||
"RetryIntervalRaw": "",
|
||||
"RetryJoinWan": [],
|
||||
"RetryMaxAttemptsWan": 0,
|
||||
"RetryIntervalWanRaw": "",
|
||||
"UiDir": "/opt/consul-ui",
|
||||
"PidFile": "",
|
||||
"EnableSyslog": true,
|
||||
"SyslogFacility": "LOCAL0",
|
||||
"RejoinAfterLeave": false,
|
||||
"CheckUpdateInterval": 300000000000,
|
||||
"ACLDatacenter": "",
|
||||
"ACLTTL": 30000000000,
|
||||
"ACLTTLRaw": "",
|
||||
"ACLDefaultPolicy": "allow",
|
||||
"ACLDownPolicy": "extend-cache",
|
||||
"Watches": null,
|
||||
"DisableRemoteExec": false,
|
||||
"DisableUpdateCheck": false,
|
||||
"DisableAnonymousSignature": false,
|
||||
"HTTPAPIResponseHeaders": null,
|
||||
"AtlasInfrastructure": "",
|
||||
"AtlasJoin": false,
|
||||
"Revision": "9a9cc9341bb487651a0399e3fc5e1e8a42e62dd9+CHANGES",
|
||||
"Version": "0.5.2",
|
||||
"VersionPrerelease": "",
|
||||
"UnixSockets": {
|
||||
"Usr": "",
|
||||
"Grp": "",
|
||||
"Perms": ""
|
||||
},
|
||||
"SessionTTLMin": 0,
|
||||
"SessionTTLMinRaw": ""
|
||||
},
|
||||
"Member": {
|
||||
"Name": "consul2",
|
||||
"Addr": "172.16.59.133",
|
||||
"Port": 8301,
|
||||
"Tags": {
|
||||
"build": "0.5.2:9a9cc934",
|
||||
"dc": "vagrant",
|
||||
"expect": "3",
|
||||
"port": "8300",
|
||||
"role": "consul",
|
||||
"segment": "mysegment",
|
||||
"vsn": "2"
|
||||
},
|
||||
"Status": 1,
|
||||
"ProtocolMin": 1,
|
||||
"ProtocolMax": 2,
|
||||
"ProtocolCur": 2,
|
||||
"DelegateMin": 2,
|
||||
"DelegateMax": 4,
|
||||
"DelegateCur": 4
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
// TestConsulFingerprint_UnexpectedResponse asserts that the Consul
|
||||
// fingerprinter does not panic when it encounters an unexpected response.
|
||||
// See https://github.com/hashicorp/nomad/issues/3326
|
||||
func TestConsulFingerprint_UnexpectedResponse(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
fp := NewConsulFingerprint(testlog.HCLogger(t))
|
||||
node := &structs.Node{
|
||||
Attributes: make(map[string]string),
|
||||
}
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprintln(w, "{}")
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
conf := config.DefaultConfig()
|
||||
conf.ConsulConfig.Addr = strings.TrimPrefix(ts.URL, "http://")
|
||||
|
||||
request := &FingerprintRequest{Config: conf, Node: node}
|
||||
var response FingerprintResponse
|
||||
err := fp.Fingerprint(request, &response)
|
||||
assert.Nil(err)
|
||||
|
||||
if !response.Detected {
|
||||
t.Fatalf("expected response to be applicable")
|
||||
}
|
||||
|
||||
attrs := []string{
|
||||
"consul.server",
|
||||
"consul.version",
|
||||
"consul.revision",
|
||||
"unique.consul.name",
|
||||
"consul.datacenter",
|
||||
"consul.segment",
|
||||
}
|
||||
|
||||
for _, attr := range attrs {
|
||||
if v, ok := response.Attributes[attr]; ok {
|
||||
t.Errorf("unexpected node attribute %q with vlaue %q", attr, v)
|
||||
if working {
|
||||
_, _ = io.WriteString(w, payload)
|
||||
working = false
|
||||
} else {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
working = true
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
if v, ok := response.Links["consul"]; ok {
|
||||
t.Errorf("Unexpected link to consul: %v", v)
|
||||
}
|
||||
cfg := config.DefaultConfig()
|
||||
cfg.ConsulConfig.Addr = strings.TrimPrefix(ts.URL, `http://`)
|
||||
return ts, cfg
|
||||
}
|
||||
|
||||
func fakeConsulPayload(t *testing.T, filename string) string {
|
||||
b, err := ioutil.ReadFile(filename)
|
||||
require.NoError(t, err)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func newConsulFingerPrint(t *testing.T) *ConsulFingerprint {
|
||||
return NewConsulFingerprint(testlog.HCLogger(t)).(*ConsulFingerprint)
|
||||
}
|
||||
|
||||
func TestConsulFingerprint_server(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fp := newConsulFingerPrint(t)
|
||||
|
||||
t.Run("is server", func(t *testing.T) {
|
||||
s, ok := fp.server(consulInfo{
|
||||
"Config": {"Server": true},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "true", s)
|
||||
})
|
||||
|
||||
t.Run("is not server", func(t *testing.T) {
|
||||
s, ok := fp.server(consulInfo{
|
||||
"Config": {"Server": false},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "false", s)
|
||||
})
|
||||
|
||||
t.Run("missing", func(t *testing.T) {
|
||||
_, ok := fp.server(consulInfo{
|
||||
"Config": {},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
t.Run("malformed", func(t *testing.T) {
|
||||
_, ok := fp.server(consulInfo{
|
||||
"Config": {"Server": 9000},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestConsulFingerprint_version(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fp := newConsulFingerPrint(t)
|
||||
|
||||
t.Run("oss", func(t *testing.T) {
|
||||
v, ok := fp.version(consulInfo{
|
||||
"Config": {"Version": "v1.9.5"},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "v1.9.5", v)
|
||||
})
|
||||
|
||||
t.Run("ent", func(t *testing.T) {
|
||||
v, ok := fp.version(consulInfo{
|
||||
"Config": {"Version": "v1.9.5+ent"},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "v1.9.5+ent", v)
|
||||
})
|
||||
|
||||
t.Run("missing", func(t *testing.T) {
|
||||
_, ok := fp.version(consulInfo{
|
||||
"Config": {},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
t.Run("malformed", func(t *testing.T) {
|
||||
_, ok := fp.version(consulInfo{
|
||||
"Config": {"Version": 9000},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestConsulFingerprint_sku(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fp := newConsulFingerPrint(t)
|
||||
|
||||
t.Run("oss", func(t *testing.T) {
|
||||
s, ok := fp.sku(consulInfo{
|
||||
"Config": {"Version": "v1.9.5"},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "oss", s)
|
||||
})
|
||||
|
||||
t.Run("oss dev", func(t *testing.T) {
|
||||
s, ok := fp.sku(consulInfo{
|
||||
"Config": {"Version": "v1.9.5-dev"},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "oss", s)
|
||||
})
|
||||
|
||||
t.Run("ent", func(t *testing.T) {
|
||||
s, ok := fp.sku(consulInfo{
|
||||
"Config": {"Version": "v1.9.5+ent"},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "ent", s)
|
||||
})
|
||||
|
||||
t.Run("ent dev", func(t *testing.T) {
|
||||
s, ok := fp.sku(consulInfo{
|
||||
"Config": {"Version": "v1.9.5+ent-dev"},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "ent", s)
|
||||
})
|
||||
|
||||
t.Run("missing", func(t *testing.T) {
|
||||
_, ok := fp.sku(consulInfo{
|
||||
"Config": {},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
t.Run("malformed", func(t *testing.T) {
|
||||
_, ok := fp.sku(consulInfo{
|
||||
"Config": {"Version": "***"},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestConsulFingerprint_revision(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fp := newConsulFingerPrint(t)
|
||||
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
r, ok := fp.revision(consulInfo{
|
||||
"Config": {"Revision": "3c1c22679"},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "3c1c22679", r)
|
||||
})
|
||||
|
||||
t.Run("malformed", func(t *testing.T) {
|
||||
_, ok := fp.revision(consulInfo{
|
||||
"Config": {"Revision": 9000},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
t.Run("missing", func(t *testing.T) {
|
||||
_, ok := fp.revision(consulInfo{
|
||||
"Config": {},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestConsulFingerprint_dc(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fp := newConsulFingerPrint(t)
|
||||
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
dc, ok := fp.dc(consulInfo{
|
||||
"Config": {"Datacenter": "dc1"},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "dc1", dc)
|
||||
})
|
||||
|
||||
t.Run("malformed", func(t *testing.T) {
|
||||
_, ok := fp.dc(consulInfo{
|
||||
"Config": {"Datacenter": 9000},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
t.Run("missing", func(t *testing.T) {
|
||||
_, ok := fp.dc(consulInfo{
|
||||
"Config": {},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestConsulFingerprint_segment(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fp := newConsulFingerPrint(t)
|
||||
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
s, ok := fp.segment(consulInfo{
|
||||
"Member": {"Tags": map[string]interface{}{"segment": "seg1"}},
|
||||
})
|
||||
require.True(t, ok)
|
||||
require.Equal(t, "seg1", s)
|
||||
})
|
||||
|
||||
t.Run("segment missing", func(t *testing.T) {
|
||||
_, ok := fp.segment(consulInfo{
|
||||
"Member": {"Tags": map[string]interface{}{}},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
t.Run("tags missing", func(t *testing.T) {
|
||||
_, ok := fp.segment(consulInfo{
|
||||
"Member": {},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
|
||||
t.Run("malformed", func(t *testing.T) {
|
||||
_, ok := fp.segment(consulInfo{
|
||||
"Member": {"Tags": map[string]interface{}{"segment": 9000}},
|
||||
})
|
||||
require.False(t, ok)
|
||||
})
|
||||
}
|
||||
|
||||
func TestConsulFingerprint_Fingerprint(t *testing.T) {
|
||||
cf := newConsulFingerPrint(t)
|
||||
|
||||
ts, cfg := fakeConsul(fakeConsulPayload(t, "test_fixtures/consul/agent_self.json"))
|
||||
defer ts.Close()
|
||||
|
||||
node := &structs.Node{Attributes: make(map[string]string)}
|
||||
|
||||
// consul not available before first run
|
||||
require.Equal(t, consulUnavailable, cf.lastState)
|
||||
|
||||
// execute first query with good response
|
||||
var resp FingerprintResponse
|
||||
err := cf.Fingerprint(&FingerprintRequest{Config: cfg, Node: node}, &resp)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, map[string]string{
|
||||
"consul.datacenter": "dc1",
|
||||
"consul.revision": "3c1c22679",
|
||||
"consul.segment": "seg1",
|
||||
"consul.server": "true",
|
||||
"consul.sku": "oss",
|
||||
"consul.version": "1.9.5",
|
||||
"unique.consul.name": "HAL9000",
|
||||
}, resp.Attributes)
|
||||
require.True(t, resp.Detected)
|
||||
|
||||
// consul now available
|
||||
require.Equal(t, consulAvailable, cf.lastState)
|
||||
|
||||
var resp2 FingerprintResponse
|
||||
|
||||
// pretend attributes set for failing request
|
||||
node.Attributes["consul.datacenter"] = "foo"
|
||||
node.Attributes["consul.revision"] = "foo"
|
||||
node.Attributes["consul.segment"] = "foo"
|
||||
node.Attributes["consul.server"] = "foo"
|
||||
node.Attributes["consul.sku"] = "foo"
|
||||
node.Attributes["consul.version"] = "foo"
|
||||
node.Attributes["unique.consul.name"] = "foo"
|
||||
|
||||
// execute second query with error
|
||||
err2 := cf.Fingerprint(&FingerprintRequest{Config: cfg, Node: node}, &resp2)
|
||||
require.NoError(t, err2) // does not return error
|
||||
require.Equal(t, map[string]string{ // attributes set empty
|
||||
"consul.datacenter": "",
|
||||
"consul.revision": "",
|
||||
"consul.segment": "",
|
||||
"consul.server": "",
|
||||
"consul.sku": "",
|
||||
"consul.version": "",
|
||||
"unique.consul.name": "",
|
||||
}, resp2.Attributes)
|
||||
require.True(t, resp.Detected) // never downgrade
|
||||
|
||||
// consul no longer available
|
||||
require.Equal(t, consulUnavailable, cf.lastState)
|
||||
|
||||
// execute third query no error
|
||||
var resp3 FingerprintResponse
|
||||
err3 := cf.Fingerprint(&FingerprintRequest{Config: cfg, Node: node}, &resp3)
|
||||
require.NoError(t, err3)
|
||||
require.Equal(t, map[string]string{
|
||||
"consul.datacenter": "dc1",
|
||||
"consul.revision": "3c1c22679",
|
||||
"consul.segment": "seg1",
|
||||
"consul.server": "true",
|
||||
"consul.sku": "oss",
|
||||
"consul.version": "1.9.5",
|
||||
"unique.consul.name": "HAL9000",
|
||||
}, resp3.Attributes)
|
||||
|
||||
// consul now available again
|
||||
require.Equal(t, consulAvailable, cf.lastState)
|
||||
require.True(t, resp.Detected)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,478 @@
|
|||
{
|
||||
"Config": {
|
||||
"Datacenter": "dc1",
|
||||
"NodeName": "HAL9000",
|
||||
"NodeID": "fa512dd3-4e92-6fb5-6446-bd7ed012ebe0",
|
||||
"Revision": "3c1c22679",
|
||||
"Server": true,
|
||||
"Version": "1.9.5"
|
||||
},
|
||||
"DebugConfig": {
|
||||
"ACLDatacenter": "dc1",
|
||||
"ACLDefaultPolicy": "allow",
|
||||
"ACLDisabledTTL": "2m0s",
|
||||
"ACLDownPolicy": "extend-cache",
|
||||
"ACLEnableKeyListPolicy": false,
|
||||
"ACLMasterToken": "hidden",
|
||||
"ACLPolicyTTL": "30s",
|
||||
"ACLRoleTTL": "0s",
|
||||
"ACLTokenReplication": false,
|
||||
"ACLTokenTTL": "30s",
|
||||
"ACLTokens": {
|
||||
"ACLAgentMasterToken": "hidden",
|
||||
"ACLAgentToken": "hidden",
|
||||
"ACLDefaultToken": "hidden",
|
||||
"ACLReplicationToken": "hidden",
|
||||
"DataDir": "",
|
||||
"EnablePersistence": false,
|
||||
"EnterpriseConfig": {}
|
||||
},
|
||||
"ACLsEnabled": false,
|
||||
"AEInterval": "1m0s",
|
||||
"AdvertiseAddrLAN": "127.0.0.1",
|
||||
"AdvertiseAddrWAN": "127.0.0.1",
|
||||
"AdvertiseReconnectTimeout": "0s",
|
||||
"AllowWriteHTTPFrom": [],
|
||||
"AutoConfig": {
|
||||
"Authorizer": {
|
||||
"AllowReuse": false,
|
||||
"AuthMethod": {
|
||||
"ACLAuthMethodEnterpriseFields": {},
|
||||
"Config": {
|
||||
"BoundAudiences": null,
|
||||
"BoundIssuer": "",
|
||||
"ClaimMappings": null,
|
||||
"ClockSkewLeeway": 0,
|
||||
"ExpirationLeeway": 0,
|
||||
"JWKSCACert": "",
|
||||
"JWKSURL": "",
|
||||
"JWTSupportedAlgs": null,
|
||||
"JWTValidationPubKeys": null,
|
||||
"ListClaimMappings": null,
|
||||
"NotBeforeLeeway": 0,
|
||||
"OIDCDiscoveryCACert": "",
|
||||
"OIDCDiscoveryURL": ""
|
||||
},
|
||||
"Description": "",
|
||||
"DisplayName": "",
|
||||
"EnterpriseMeta": {},
|
||||
"MaxTokenTTL": "0s",
|
||||
"Name": "Auto Config Authorizer",
|
||||
"RaftIndex": {
|
||||
"CreateIndex": 0,
|
||||
"ModifyIndex": 0
|
||||
},
|
||||
"TokenLocality": "",
|
||||
"Type": "jwt"
|
||||
},
|
||||
"ClaimAssertions": [],
|
||||
"Enabled": false
|
||||
},
|
||||
"DNSSANs": [],
|
||||
"Enabled": false,
|
||||
"IPSANs": [],
|
||||
"IntroToken": "hidden",
|
||||
"IntroTokenFile": "",
|
||||
"ServerAddresses": []
|
||||
},
|
||||
"AutoEncryptAllowTLS": false,
|
||||
"AutoEncryptDNSSAN": [],
|
||||
"AutoEncryptIPSAN": [],
|
||||
"AutoEncryptTLS": false,
|
||||
"AutopilotCleanupDeadServers": true,
|
||||
"AutopilotDisableUpgradeMigration": false,
|
||||
"AutopilotLastContactThreshold": "200ms",
|
||||
"AutopilotMaxTrailingLogs": 250,
|
||||
"AutopilotMinQuorum": 0,
|
||||
"AutopilotRedundancyZoneTag": "",
|
||||
"AutopilotServerStabilizationTime": "10s",
|
||||
"AutopilotUpgradeVersionTag": "",
|
||||
"BindAddr": "127.0.0.1",
|
||||
"Bootstrap": false,
|
||||
"BootstrapExpect": 0,
|
||||
"CAFile": "",
|
||||
"CAPath": "",
|
||||
"Cache": {
|
||||
"EntryFetchMaxBurst": 2,
|
||||
"EntryFetchRate": 1.7976931348623157e+308,
|
||||
"Logger": null
|
||||
},
|
||||
"CertFile": "",
|
||||
"CheckDeregisterIntervalMin": "1m0s",
|
||||
"CheckOutputMaxSize": 4096,
|
||||
"CheckReapInterval": "30s",
|
||||
"CheckUpdateInterval": "5m0s",
|
||||
"Checks": [],
|
||||
"ClientAddrs": [
|
||||
"127.0.0.1"
|
||||
],
|
||||
"ConfigEntryBootstrap": [],
|
||||
"ConnectCAConfig": {},
|
||||
"ConnectCAProvider": "",
|
||||
"ConnectEnabled": true,
|
||||
"ConnectMeshGatewayWANFederationEnabled": false,
|
||||
"ConnectSidecarMaxPort": 21255,
|
||||
"ConnectSidecarMinPort": 21000,
|
||||
"ConnectTestCALeafRootChangeSpread": "0s",
|
||||
"ConsulCoordinateUpdateBatchSize": 128,
|
||||
"ConsulCoordinateUpdateMaxBatches": 5,
|
||||
"ConsulCoordinateUpdatePeriod": "100ms",
|
||||
"ConsulRaftElectionTimeout": "52ms",
|
||||
"ConsulRaftHeartbeatTimeout": "35ms",
|
||||
"ConsulRaftLeaderLeaseTimeout": "20ms",
|
||||
"ConsulServerHealthInterval": "10ms",
|
||||
"DNSARecordLimit": 0,
|
||||
"DNSAddrs": [
|
||||
"tcp://127.0.0.1:8600",
|
||||
"udp://127.0.0.1:8600"
|
||||
],
|
||||
"DNSAllowStale": true,
|
||||
"DNSAltDomain": "",
|
||||
"DNSCacheMaxAge": "0s",
|
||||
"DNSDisableCompression": false,
|
||||
"DNSDomain": "consul.",
|
||||
"DNSEnableTruncate": false,
|
||||
"DNSMaxStale": "87600h0m0s",
|
||||
"DNSNodeMetaTXT": true,
|
||||
"DNSNodeTTL": "0s",
|
||||
"DNSOnlyPassing": false,
|
||||
"DNSPort": 8600,
|
||||
"DNSRecursorTimeout": "2s",
|
||||
"DNSRecursors": [],
|
||||
"DNSSOA": {
|
||||
"Expire": 86400,
|
||||
"Minttl": 0,
|
||||
"Refresh": 3600,
|
||||
"Retry": 600
|
||||
},
|
||||
"DNSServiceTTL": {},
|
||||
"DNSUDPAnswerLimit": 3,
|
||||
"DNSUseCache": false,
|
||||
"DataDir": "",
|
||||
"Datacenter": "dc1",
|
||||
"DefaultQueryTime": "5m0s",
|
||||
"DevMode": true,
|
||||
"DisableAnonymousSignature": true,
|
||||
"DisableCoordinates": false,
|
||||
"DisableHTTPUnprintableCharFilter": false,
|
||||
"DisableHostNodeID": true,
|
||||
"DisableKeyringFile": true,
|
||||
"DisableRemoteExec": true,
|
||||
"DisableUpdateCheck": false,
|
||||
"DiscardCheckOutput": false,
|
||||
"DiscoveryMaxStale": "0s",
|
||||
"EnableAgentTLSForChecks": false,
|
||||
"EnableCentralServiceConfig": true,
|
||||
"EnableDebug": true,
|
||||
"EnableLocalScriptChecks": false,
|
||||
"EnableRemoteScriptChecks": false,
|
||||
"EncryptKey": "hidden",
|
||||
"EncryptVerifyIncoming": true,
|
||||
"EncryptVerifyOutgoing": true,
|
||||
"EnterpriseRuntimeConfig": {},
|
||||
"ExposeMaxPort": 21755,
|
||||
"ExposeMinPort": 21500,
|
||||
"GRPCAddrs": [
|
||||
"tcp://127.0.0.1:8502"
|
||||
],
|
||||
"GRPCPort": 8502,
|
||||
"GossipLANGossipInterval": "100ms",
|
||||
"GossipLANGossipNodes": 3,
|
||||
"GossipLANProbeInterval": "100ms",
|
||||
"GossipLANProbeTimeout": "100ms",
|
||||
"GossipLANRetransmitMult": 4,
|
||||
"GossipLANSuspicionMult": 3,
|
||||
"GossipWANGossipInterval": "100ms",
|
||||
"GossipWANGossipNodes": 3,
|
||||
"GossipWANProbeInterval": "100ms",
|
||||
"GossipWANProbeTimeout": "100ms",
|
||||
"GossipWANRetransmitMult": 4,
|
||||
"GossipWANSuspicionMult": 3,
|
||||
"HTTPAddrs": [
|
||||
"tcp://127.0.0.1:8500"
|
||||
],
|
||||
"HTTPBlockEndpoints": [],
|
||||
"HTTPMaxConnsPerClient": 200,
|
||||
"HTTPMaxHeaderBytes": 0,
|
||||
"HTTPPort": 8500,
|
||||
"HTTPResponseHeaders": {},
|
||||
"HTTPSAddrs": [],
|
||||
"HTTPSHandshakeTimeout": "5s",
|
||||
"HTTPSPort": -1,
|
||||
"HTTPUseCache": true,
|
||||
"KVMaxValueSize": 524288,
|
||||
"KeyFile": "hidden",
|
||||
"LeaveDrainTime": "5s",
|
||||
"LeaveOnTerm": false,
|
||||
"Logging": {
|
||||
"EnableSyslog": false,
|
||||
"LogFilePath": "",
|
||||
"LogJSON": false,
|
||||
"LogLevel": "DEBUG",
|
||||
"LogRotateBytes": 0,
|
||||
"LogRotateDuration": "0s",
|
||||
"LogRotateMaxFiles": 0,
|
||||
"Name": "",
|
||||
"SyslogFacility": "LOCAL0"
|
||||
},
|
||||
"MaxQueryTime": "10m0s",
|
||||
"NodeID": "fa512dd3-4e92-6fb5-6446-bd7ed012ebe0",
|
||||
"NodeMeta": {},
|
||||
"NodeName": "x52",
|
||||
"PidFile": "",
|
||||
"PrimaryDatacenter": "dc1",
|
||||
"PrimaryGateways": [],
|
||||
"PrimaryGatewaysInterval": "30s",
|
||||
"RPCAdvertiseAddr": "tcp://127.0.0.1:8300",
|
||||
"RPCBindAddr": "tcp://127.0.0.1:8300",
|
||||
"RPCConfig": {
|
||||
"EnableStreaming": false
|
||||
},
|
||||
"RPCHandshakeTimeout": "5s",
|
||||
"RPCHoldTimeout": "7s",
|
||||
"RPCMaxBurst": 1000,
|
||||
"RPCMaxConnsPerClient": 100,
|
||||
"RPCProtocol": 2,
|
||||
"RPCRateLimit": -1,
|
||||
"RaftProtocol": 3,
|
||||
"RaftSnapshotInterval": "0s",
|
||||
"RaftSnapshotThreshold": 0,
|
||||
"RaftTrailingLogs": 0,
|
||||
"ReadReplica": false,
|
||||
"ReconnectTimeoutLAN": "0s",
|
||||
"ReconnectTimeoutWAN": "0s",
|
||||
"RejoinAfterLeave": false,
|
||||
"RetryJoinIntervalLAN": "30s",
|
||||
"RetryJoinIntervalWAN": "30s",
|
||||
"RetryJoinLAN": [],
|
||||
"RetryJoinMaxAttemptsLAN": 0,
|
||||
"RetryJoinMaxAttemptsWAN": 0,
|
||||
"RetryJoinWAN": [],
|
||||
"Revision": "",
|
||||
"SegmentLimit": 64,
|
||||
"SegmentName": "",
|
||||
"SegmentNameLimit": 64,
|
||||
"Segments": [],
|
||||
"SerfAdvertiseAddrLAN": "tcp://127.0.0.1:8301",
|
||||
"SerfAdvertiseAddrWAN": "tcp://127.0.0.1:8302",
|
||||
"SerfAllowedCIDRsLAN": [],
|
||||
"SerfAllowedCIDRsWAN": [],
|
||||
"SerfBindAddrLAN": "tcp://127.0.0.1:8301",
|
||||
"SerfBindAddrWAN": "tcp://127.0.0.1:8302",
|
||||
"SerfPortLAN": 8301,
|
||||
"SerfPortWAN": 8302,
|
||||
"ServerMode": true,
|
||||
"ServerName": "",
|
||||
"ServerPort": 8300,
|
||||
"Services": [],
|
||||
"SessionTTLMin": "0s",
|
||||
"SkipLeaveOnInt": true,
|
||||
"StartJoinAddrsLAN": [],
|
||||
"StartJoinAddrsWAN": [],
|
||||
"SyncCoordinateIntervalMin": "15s",
|
||||
"SyncCoordinateRateTarget": 64,
|
||||
"TLSCipherSuites": [],
|
||||
"TLSMinVersion": "tls12",
|
||||
"TLSPreferServerCipherSuites": false,
|
||||
"TaggedAddresses": {
|
||||
"lan": "127.0.0.1",
|
||||
"lan_ipv4": "127.0.0.1",
|
||||
"wan": "127.0.0.1",
|
||||
"wan_ipv4": "127.0.0.1"
|
||||
},
|
||||
"Telemetry": {
|
||||
"AllowedPrefixes": [],
|
||||
"BlockedPrefixes": [],
|
||||
"CirconusAPIApp": "",
|
||||
"CirconusAPIToken": "hidden",
|
||||
"CirconusAPIURL": "",
|
||||
"CirconusBrokerID": "",
|
||||
"CirconusBrokerSelectTag": "",
|
||||
"CirconusCheckDisplayName": "",
|
||||
"CirconusCheckForceMetricActivation": "",
|
||||
"CirconusCheckID": "",
|
||||
"CirconusCheckInstanceID": "",
|
||||
"CirconusCheckSearchTag": "",
|
||||
"CirconusCheckTags": "",
|
||||
"CirconusSubmissionInterval": "",
|
||||
"CirconusSubmissionURL": "",
|
||||
"Disable": false,
|
||||
"DisableCompatOneNine": false,
|
||||
"DisableHostname": false,
|
||||
"DogstatsdAddr": "",
|
||||
"DogstatsdTags": [],
|
||||
"FilterDefault": true,
|
||||
"MetricsPrefix": "consul",
|
||||
"PrometheusOpts": {
|
||||
"CounterDefinitions": [],
|
||||
"Expiration": "0s",
|
||||
"GaugeDefinitions": [],
|
||||
"Registerer": null,
|
||||
"SummaryDefinitions": []
|
||||
},
|
||||
"StatsdAddr": "",
|
||||
"StatsiteAddr": ""
|
||||
},
|
||||
"TranslateWANAddrs": false,
|
||||
"TxnMaxReqLen": 524288,
|
||||
"UIConfig": {
|
||||
"ContentPath": "/ui/",
|
||||
"DashboardURLTemplates": {},
|
||||
"Dir": "",
|
||||
"Enabled": true,
|
||||
"MetricsProvider": "",
|
||||
"MetricsProviderFiles": [],
|
||||
"MetricsProviderOptionsJSON": "",
|
||||
"MetricsProxy": {
|
||||
"AddHeaders": [],
|
||||
"BaseURL": "",
|
||||
"PathAllowlist": []
|
||||
}
|
||||
},
|
||||
"UnixSocketGroup": "",
|
||||
"UnixSocketMode": "",
|
||||
"UnixSocketUser": "",
|
||||
"UseStreamingBackend": false,
|
||||
"VerifyIncoming": false,
|
||||
"VerifyIncomingHTTPS": false,
|
||||
"VerifyIncomingRPC": false,
|
||||
"VerifyOutgoing": false,
|
||||
"VerifyServerHostname": false,
|
||||
"Version": "1.9.5",
|
||||
"VersionPrerelease": "",
|
||||
"Watches": []
|
||||
},
|
||||
"Coord": {
|
||||
"Vec": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"Error": 1.5,
|
||||
"Adjustment": 0,
|
||||
"Height": 0.00001
|
||||
},
|
||||
"Member": {
|
||||
"Name": "x52",
|
||||
"Addr": "127.0.0.1",
|
||||
"Port": 8301,
|
||||
"Tags": {
|
||||
"acls": "0",
|
||||
"build": "1.9.5:",
|
||||
"dc": "dc1",
|
||||
"ft_fs": "1",
|
||||
"ft_si": "1",
|
||||
"id": "fa512dd3-4e92-6fb5-6446-bd7ed012ebe0",
|
||||
"port": "8300",
|
||||
"raft_vsn": "3",
|
||||
"role": "consul",
|
||||
"segment": "seg1",
|
||||
"vsn": "2",
|
||||
"vsn_max": "3",
|
||||
"vsn_min": "2",
|
||||
"wan_join_port": "8302"
|
||||
},
|
||||
"Status": 1,
|
||||
"ProtocolMin": 1,
|
||||
"ProtocolMax": 5,
|
||||
"ProtocolCur": 2,
|
||||
"DelegateMin": 2,
|
||||
"DelegateMax": 5,
|
||||
"DelegateCur": 4
|
||||
},
|
||||
"Stats": {
|
||||
"agent": {
|
||||
"check_monitors": "0",
|
||||
"check_ttls": "0",
|
||||
"checks": "0",
|
||||
"services": "0"
|
||||
},
|
||||
"build": {
|
||||
"prerelease": "",
|
||||
"revision": "",
|
||||
"version": "1.9.5"
|
||||
},
|
||||
"consul": {
|
||||
"acl": "disabled",
|
||||
"bootstrap": "false",
|
||||
"known_datacenters": "1",
|
||||
"leader": "true",
|
||||
"leader_addr": "127.0.0.1:8300",
|
||||
"server": "true"
|
||||
},
|
||||
"raft": {
|
||||
"applied_index": "13",
|
||||
"commit_index": "13",
|
||||
"fsm_pending": "0",
|
||||
"last_contact": "0",
|
||||
"last_log_index": "13",
|
||||
"last_log_term": "2",
|
||||
"last_snapshot_index": "0",
|
||||
"last_snapshot_term": "0",
|
||||
"latest_configuration": "[{Suffrage:Voter ID:fa512dd3-4e92-6fb5-6446-bd7ed012ebe0 Address:127.0.0.1:8300}]",
|
||||
"latest_configuration_index": "0",
|
||||
"num_peers": "0",
|
||||
"protocol_version": "3",
|
||||
"protocol_version_max": "3",
|
||||
"protocol_version_min": "0",
|
||||
"snapshot_version_max": "1",
|
||||
"snapshot_version_min": "0",
|
||||
"state": "Leader",
|
||||
"term": "2"
|
||||
},
|
||||
"runtime": {
|
||||
"arch": "amd64",
|
||||
"cpu_count": "24",
|
||||
"goroutines": "85",
|
||||
"max_procs": "24",
|
||||
"os": "linux",
|
||||
"version": "go1.16.4"
|
||||
},
|
||||
"serf_lan": {
|
||||
"coordinate_resets": "0",
|
||||
"encrypted": "false",
|
||||
"event_queue": "1",
|
||||
"event_time": "2",
|
||||
"failed": "0",
|
||||
"health_score": "0",
|
||||
"intent_queue": "0",
|
||||
"left": "0",
|
||||
"member_time": "1",
|
||||
"members": "1",
|
||||
"query_queue": "0",
|
||||
"query_time": "1"
|
||||
},
|
||||
"serf_wan": {
|
||||
"coordinate_resets": "0",
|
||||
"encrypted": "false",
|
||||
"event_queue": "0",
|
||||
"event_time": "1",
|
||||
"failed": "0",
|
||||
"health_score": "0",
|
||||
"intent_queue": "0",
|
||||
"left": "0",
|
||||
"member_time": "1",
|
||||
"members": "1",
|
||||
"query_queue": "0",
|
||||
"query_time": "1"
|
||||
}
|
||||
},
|
||||
"Meta": {
|
||||
"consul-network-segment": ""
|
||||
},
|
||||
"xDS": {
|
||||
"SupportedProxies": {
|
||||
"envoy": [
|
||||
"1.16.2",
|
||||
"1.15.3",
|
||||
"1.14.6",
|
||||
"1.13.7"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue