Merge pull request #10699 from hashicorp/f-consul-fp

fingerprint: update consul fingerprinter with additional attributes
This commit is contained in:
Seth Hoenig
2021-06-03 15:14:25 -05:00
committed by GitHub
5 changed files with 1661 additions and 259 deletions

View File

@@ -3,6 +3,7 @@
IMPROVEMENTS:
* cli: Added success confirmation message for `nomad volume delete` and `nomad volume deregister`. [[GH-10591](https://github.com/hashicorp/nomad/issues/10591)]
* cli: Cross-namespace `nomad job` commands will now select exact matches if the selection is unambiguous. [[GH-10648](https://github.com/hashicorp/nomad/issues/10648)]
* client/fingerprint: Consul fingerprinter probes for additional enterprise and connect related attributes [[GH-10699](https://github.com/hashicorp/nomad/pull/10699)]
* csi: Validate that `volume` blocks for CSI volumes include the required `attachment_mode` and `access_mode` fields. [[GH-10651](https://github.com/hashicorp/nomad/issues/10651)]
BUG FIXES:

View File

@@ -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,123 @@ 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,
"consul.connect": f.connect,
"consul.grpc": f.grpc,
"consul.ft.namespaces": f.namespaces,
}
}
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 +142,90 @@ 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
}
func (f *ConsulFingerprint) connect(info consulInfo) (string, bool) {
c, ok := info["DebugConfig"]["ConnectEnabled"].(bool)
return strconv.FormatBool(c), ok
}
func (f *ConsulFingerprint) grpc(info consulInfo) (string, bool) {
p, ok := info["DebugConfig"]["GRPCPort"].(float64)
return fmt.Sprintf("%d", int(p)), ok
}
func (f *ConsulFingerprint) namespaces(info consulInfo) (string, bool) {
return f.feature("Namespaces", info)
}
// possible values as of v1.9.5+ent:
// Automated Backups, Automated Upgrades, Enhanced Read Scalability,
// Network Segments, Redundancy Zone, Advanced Network Federation,
// Namespaces, SSO, Audit Logging
func (f *ConsulFingerprint) feature(name string, info consulInfo) (string, bool) {
lic, licOK := info["Stats"]["license"].(map[string]interface{})
if !licOK {
return "", false
}
features, exists := lic["features"].(string)
if !exists {
return "", false
}
if !strings.Contains(features, name) {
return "", false
}
return "true", true
}

View File

@@ -1,7 +1,8 @@
package fingerprint
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
@@ -10,207 +11,519 @@ 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_connect(t *testing.T) {
t.Parallel()
fp := newConsulFingerPrint(t)
t.Run("connect enabled", func(t *testing.T) {
s, ok := fp.connect(consulInfo{
"DebugConfig": {"ConnectEnabled": true},
})
require.True(t, ok)
require.Equal(t, "true", s)
})
t.Run("connect not enabled", func(t *testing.T) {
s, ok := fp.connect(consulInfo{
"DebugConfig": {"ConnectEnabled": false},
})
require.True(t, ok)
require.Equal(t, "false", s)
})
t.Run("connect missing", func(t *testing.T) {
_, ok := fp.connect(consulInfo{
"DebugConfig": {},
})
require.False(t, ok)
})
}
func TestConsulFingerprint_grpc(t *testing.T) {
t.Parallel()
fp := newConsulFingerPrint(t)
t.Run("grpc set", func(t *testing.T) {
s, ok := fp.grpc(consulInfo{
"DebugConfig": {"GRPCPort": 8502.0}, // JSON numbers are floats
})
require.True(t, ok)
require.Equal(t, "8502", s)
})
t.Run("grpc disabled", func(t *testing.T) {
s, ok := fp.grpc(consulInfo{
"DebugConfig": {"GRPCPort": -1.0}, // JSON numbers are floats
})
require.True(t, ok)
require.Equal(t, "-1", s)
})
t.Run("grpc missing", func(t *testing.T) {
_, ok := fp.grpc(consulInfo{
"DebugConfig": {},
})
require.False(t, ok)
})
}
func TestConsulFingerprint_namespaces(t *testing.T) {
t.Parallel()
fp := newConsulFingerPrint(t)
t.Run("supports namespaces", func(t *testing.T) {
s, ok := fp.namespaces(consulInfo{
"Stats": {"license": map[string]interface{}{"features": "Automated Backups, Automated Upgrades, Enhanced Read Scalability, Network Segments, Redundancy Zone, Advanced Network Federation, Namespaces, SSO, Audit Logging"}},
})
require.True(t, ok)
require.Equal(t, "true", s)
})
t.Run("no namespaces", func(t *testing.T) {
_, ok := fp.namespaces(consulInfo{
"Stats": {"license": map[string]interface{}{"features": "Automated Backups, Automated Upgrades, Enhanced Read Scalability, Network Segments, Redundancy Zone, Advanced Network Federation, SSO, Audit Logging"}},
})
require.False(t, ok)
})
t.Run("stats missing", func(t *testing.T) {
_, ok := fp.namespaces(consulInfo{})
require.False(t, ok)
})
t.Run("license missing", func(t *testing.T) {
_, ok := fp.namespaces(consulInfo{"Stats": {}})
require.False(t, ok)
})
t.Run("features missing", func(t *testing.T) {
_, ok := fp.namespaces(consulInfo{"Stats": {"license": map[string]interface{}{}}})
require.False(t, ok)
})
}
func TestConsulFingerprint_Fingerprint_oss(t *testing.T) {
cf := newConsulFingerPrint(t)
ts, cfg := fakeConsul(fakeConsulPayload(t, "test_fixtures/consul/agent_self_oss.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",
"consul.connect": "true",
"consul.grpc": "8502",
"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["consul.connect"] = "foo"
node.Attributes["connect.grpc"] = "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": "",
"consul.connect": "",
"consul.grpc": "",
"consul.ft.namespaces": "",
}, 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",
"consul.connect": "true",
"consul.grpc": "8502",
"unique.consul.name": "HAL9000",
}, resp3.Attributes)
// consul now available again
require.Equal(t, consulAvailable, cf.lastState)
require.True(t, resp.Detected)
}
func TestConsulFingerprint_Fingerprint_ent(t *testing.T) {
cf := newConsulFingerPrint(t)
ts, cfg := fakeConsul(fakeConsulPayload(t, "test_fixtures/consul/agent_self_ent.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": "22ce6c6ad",
"consul.segment": "seg1",
"consul.server": "true",
"consul.sku": "ent",
"consul.version": "1.9.5+ent",
"consul.ft.namespaces": "true",
"consul.connect": "true",
"consul.grpc": "8502",
"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["consul.ft.namespaces"] = "foo"
node.Attributes["consul.connect"] = "foo"
node.Attributes["connect.grpc"] = "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": "",
"consul.ft.namespaces": "",
"consul.connect": "",
"consul.grpc": "",
"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": "22ce6c6ad",
"consul.segment": "seg1",
"consul.server": "true",
"consul.sku": "ent",
"consul.version": "1.9.5+ent",
"consul.ft.namespaces": "true",
"consul.connect": "true",
"consul.grpc": "8502",
"unique.consul.name": "HAL9000",
}, resp3.Attributes)
// consul now available again
require.Equal(t, consulAvailable, cf.lastState)
require.True(t, resp.Detected)
}

View File

@@ -0,0 +1,502 @@
{
"Config": {
"Datacenter": "dc1",
"NodeName": "HAL9000",
"NodeID": "39588353-8c00-d756-ce83-9ee70bfa5b3a",
"Revision": "22ce6c6ad",
"Server": true,
"Version": "1.9.5+ent"
},
"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": {
"ACLServiceProviderTokens": []
}
},
"ACLsEnabled": false,
"AEInterval": "1m0s",
"AdvertiseAddrLAN": "127.0.0.1",
"AdvertiseAddrWAN": "127.0.0.1",
"AdvertiseReconnectTimeout": "0s",
"AllowWriteHTTPFrom": [],
"AutoConfig": {
"Authorizer": {
"AllowReuse": false,
"AuthMethod": {
"ACLAuthMethodEnterpriseFields": {
"NamespaceRules": []
},
"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": {
"Namespace": "default"
},
"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": true,
"DiscardCheckOutput": false,
"DiscoveryMaxStale": "0s",
"EnableAgentTLSForChecks": false,
"EnableCentralServiceConfig": true,
"EnableDebug": true,
"EnableLocalScriptChecks": false,
"EnableRemoteScriptChecks": false,
"EncryptKey": "hidden",
"EncryptVerifyIncoming": true,
"EncryptVerifyOutgoing": true,
"EnterpriseRuntimeConfig": {
"ACLMSPDisableBootstrap": false,
"AuditEnabled": false,
"AuditSinks": [],
"DNSPreferNamespace": false
},
"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": "39588353-8c00-d756-ce83-9ee70bfa5b3a",
"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": "22ce6c6ad",
"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+ent",
"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+ent:22ce6c6a",
"dc": "dc1",
"ft_fs": "1",
"ft_ns": "1",
"ft_si": "1",
"id": "39588353-8c00-d756-ce83-9ee70bfa5b3a",
"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": "22ce6c6a",
"version": "1.9.5+ent"
},
"consul": {
"acl": "disabled",
"bootstrap": "false",
"known_datacenters": "1",
"leader": "true",
"leader_addr": "127.0.0.1:8300",
"server": "true"
},
"license": {
"customer": "temporary",
"expiration_time": "2021-06-03 16:44:05.553068285 -0500 CDT",
"features": "Automated Backups, Automated Upgrades, Enhanced Read Scalability, Network Segments, Redundancy Zone, Advanced Network Federation, Namespaces, SSO, Audit Logging",
"id": "temporary",
"install_id": "*",
"issue_time": "2021-06-03 10:44:05.553068285 -0500 CDT",
"modules": "",
"package": "premium",
"product": "consul",
"start_time": "2021-06-03 10:39:05.553068285 -0500 CDT"
},
"raft": {
"applied_index": "14",
"commit_index": "14",
"fsm_pending": "0",
"last_contact": "0",
"last_log_index": "14",
"last_log_term": "2",
"last_snapshot_index": "0",
"last_snapshot_term": "0",
"latest_configuration": "[{Suffrage:Voter ID:39588353-8c00-d756-ce83-9ee70bfa5b3a 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": "96",
"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"
]
}
}
}

View File

@@ -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"
]
}
}
}