From d69b713d4d069b865027fca366fd267967d8e318 Mon Sep 17 00:00:00 2001 From: Tim Gross Date: Thu, 14 Dec 2023 14:48:17 -0500 Subject: [PATCH] consul: fingerprint Consul Enterprise admin partitions Consul Enterprise agents all belong to an admin partition. Fingerprint this attribute when available. When a Consul agent is not explicitly configured with "default" it is in the default partition but will not report this in its `/v1/agent/self` endpoint. Fallback to "default" when missing only for Consul Enterprise. This feature provides users the ability to add constraints for jobs to land on Nomad nodes that have a Consul in that partition. Or it can allow cluster administrators to pair Consul partitions 1:1 with Nomad node pools. We'll also have the option to implement a future `partition` field in the jobspec's `consul` block to create an implicit constraint. Ref: https://github.com/hashicorp/nomad/issues/13139#issuecomment-1856479581 --- .changelog/19485.txt | 3 ++ client/fingerprint/consul.go | 14 +++++++++ client/fingerprint/consul_test.go | 48 +++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 .changelog/19485.txt diff --git a/.changelog/19485.txt b/.changelog/19485.txt new file mode 100644 index 00000000000..ff426cf8bf2 --- /dev/null +++ b/.changelog/19485.txt @@ -0,0 +1,3 @@ +```release-note:improvement +consul: Add fingerprint for Consul Enterprise admin partitions +``` diff --git a/client/fingerprint/consul.go b/client/fingerprint/consul.go index 634ede1e68e..3e00fe1a4c6 100644 --- a/client/fingerprint/consul.go +++ b/client/fingerprint/consul.go @@ -164,6 +164,7 @@ func (cfs *consulFingerprintState) initialize(cfg *config.ConsulConfig, logger h "consul.connect": cfs.connect, "consul.grpc": cfs.grpc(consulConfig.Scheme, logger), "consul.ft.namespaces": cfs.namespaces, + "consul.partition": cfs.partition, } } else { cfs.extractors = map[string]consulExtractor{ @@ -176,6 +177,7 @@ func (cfs *consulFingerprintState) initialize(cfg *config.ConsulConfig, logger h fmt.Sprintf("consul.%s.connect", cfg.Name): cfs.connect, fmt.Sprintf("consul.%s.grpc", cfg.Name): cfs.grpc(consulConfig.Scheme, logger), fmt.Sprintf("consul.%s.ft.namespaces", cfg.Name): cfs.namespaces, + fmt.Sprintf("consul.%s.partition", cfg.Name): cfs.partition, } } @@ -299,3 +301,15 @@ func (cfs *consulFingerprintState) grpcTLSPort(info agentconsul.Self) (string, b func (cfs *consulFingerprintState) namespaces(info agentconsul.Self) (string, bool) { return strconv.FormatBool(agentconsul.Namespaces(info)), true } + +func (cfs *consulFingerprintState) partition(info agentconsul.Self) (string, bool) { + sku, ok := agentconsul.SKU(info) + if ok && sku == "ent" { + p, ok := info["Config"]["Partition"].(string) + if !ok { + p = "default" + } + return p, true + } + return "", false +} diff --git a/client/fingerprint/consul_test.go b/client/fingerprint/consul_test.go index 3bc7db5fe8d..dda6caaf33a 100644 --- a/client/fingerprint/consul_test.go +++ b/client/fingerprint/consul_test.go @@ -445,6 +445,52 @@ func TestConsulFingerprint_namespaces(t *testing.T) { }) } +func TestConsulFingerprint_partition(t *testing.T) { + ci.Parallel(t) + + cfs := consulFingerprintState{} + + t.Run("oss", func(t *testing.T) { + p, ok := cfs.partition(agentconsul.Self{ + "Config": {"Version": "v1.9.5"}, + }) + must.False(t, ok) + must.Eq(t, "", p) + }) + + t.Run("ent default partition", func(t *testing.T) { + p, ok := cfs.partition(agentconsul.Self{ + "Config": {"Version": "v1.9.5+ent"}, + }) + must.True(t, ok) + must.Eq(t, "default", p) + }) + + t.Run("ent nondefault partition", func(t *testing.T) { + p, ok := cfs.partition(agentconsul.Self{ + "Config": {"Version": "v1.9.5+ent", "Partition": "test"}, + }) + must.True(t, ok) + must.Eq(t, "test", p) + }) + + t.Run("missing", func(t *testing.T) { + p, ok := cfs.partition(agentconsul.Self{ + "Config": {}, + }) + must.False(t, ok) + must.Eq(t, "", p) + }) + + t.Run("malformed", func(t *testing.T) { + p, ok := cfs.partition(agentconsul.Self{ + "Config": {"Version": "***"}, + }) + must.False(t, ok) + must.Eq(t, "", p) + }) +} + func TestConsulFingerprint_Fingerprint_oss(t *testing.T) { ci.Parallel(t) @@ -554,6 +600,7 @@ func TestConsulFingerprint_Fingerprint_ent(t *testing.T) { "consul.ft.namespaces": "true", "consul.connect": "true", "consul.grpc": "8502", + "consul.partition": "default", "unique.consul.name": "HAL9000", }, resp.Attributes) must.True(t, resp.Detected) @@ -602,6 +649,7 @@ func TestConsulFingerprint_Fingerprint_ent(t *testing.T) { "consul.ft.namespaces": "true", "consul.connect": "true", "consul.grpc": "8502", + "consul.partition": "default", "unique.consul.name": "HAL9000", }, resp3.Attributes)