diff --git a/.changelog/16705.txt b/.changelog/16705.txt new file mode 100644 index 000000000..d769f99bf --- /dev/null +++ b/.changelog/16705.txt @@ -0,0 +1,3 @@ +```release-note:improvement +connect: Added support for meta field on sidecar service block +``` diff --git a/api/consul.go b/api/consul.go index a70a80984..5c8677580 100644 --- a/api/consul.go +++ b/api/consul.go @@ -61,10 +61,11 @@ func (cc *ConsulConnect) Canonicalize() { // ConsulSidecarService represents a Consul Connect SidecarService jobspec // block. type ConsulSidecarService struct { - Tags []string `hcl:"tags,optional"` - Port string `hcl:"port,optional"` - Proxy *ConsulProxy `hcl:"proxy,block"` - DisableDefaultTCPCheck bool `mapstructure:"disable_default_tcp_check" hcl:"disable_default_tcp_check,optional"` + Tags []string `hcl:"tags,optional"` + Port string `hcl:"port,optional"` + Proxy *ConsulProxy `hcl:"proxy,block"` + DisableDefaultTCPCheck bool `mapstructure:"disable_default_tcp_check" hcl:"disable_default_tcp_check,optional"` + Meta map[string]string `hcl:"meta,block"` } func (css *ConsulSidecarService) Canonicalize() { @@ -76,6 +77,10 @@ func (css *ConsulSidecarService) Canonicalize() { css.Tags = nil } + if len(css.Meta) == 0 { + css.Meta = nil + } + css.Proxy.Canonicalize() } diff --git a/api/consul_test.go b/api/consul_test.go index bc714b65d..a0cc39e42 100644 --- a/api/consul_test.go +++ b/api/consul_test.go @@ -107,6 +107,9 @@ func TestConsulSidecarService_Canonicalize(t *testing.T) { LocalServiceAddress: "lsa", LocalServicePort: 80, }, + Meta: map[string]string{ + "test-key": "test-value", + }, } css.Canonicalize() must.Eq(t, &ConsulSidecarService{ @@ -115,6 +118,9 @@ func TestConsulSidecarService_Canonicalize(t *testing.T) { Proxy: &ConsulProxy{ LocalServiceAddress: "lsa", LocalServicePort: 80}, + Meta: map[string]string{ + "test-key": "test-value", + }, }, css) }) } diff --git a/command/agent/consul/connect.go b/command/agent/consul/connect.go index 3751de2c3..91f9f712f 100644 --- a/command/agent/consul/connect.go +++ b/command/agent/consul/connect.go @@ -129,6 +129,7 @@ func connectSidecarRegistration(serviceID string, info structs.AllocInfo, css *s Address: cMapping.HostIP, Proxy: proxy, Checks: checks, + Meta: maps.Clone(css.Meta), }, nil } diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index 2f68678d4..e998b9c03 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -1645,6 +1645,7 @@ func apiConnectSidecarServiceToStructs(in *api.ConsulSidecarService) *structs.Co Tags: slices.Clone(in.Tags), Proxy: apiConnectSidecarServiceProxyToStructs(in.Proxy), DisableDefaultTCPCheck: in.DisableDefaultTCPCheck, + Meta: maps.Clone(in.Meta), } } diff --git a/command/agent/job_endpoint_test.go b/command/agent/job_endpoint_test.go index 95c5e72c5..907639102 100644 --- a/command/agent/job_endpoint_test.go +++ b/command/agent/job_endpoint_test.go @@ -2557,6 +2557,9 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) { Tags: []string{"f", "g"}, Port: "9000", DisableDefaultTCPCheck: true, + Meta: map[string]string{ + "test-key": "test-value", + }, }, }, }, @@ -2965,6 +2968,9 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) { Tags: []string{"f", "g"}, Port: "9000", DisableDefaultTCPCheck: true, + Meta: map[string]string{ + "test-key": "test-value", + }, }, }, }, @@ -3766,12 +3772,18 @@ func TestConversion_apiConnectSidecarServiceToStructs(t *testing.T) { Proxy: &structs.ConsulProxy{ LocalServiceAddress: "192.168.30.1", }, + Meta: map[string]string{ + "test-key": "test-value", + }, }, apiConnectSidecarServiceToStructs(&api.ConsulSidecarService{ Tags: []string{"foo"}, Port: "myPort", Proxy: &api.ConsulProxy{ LocalServiceAddress: "192.168.30.1", }, + Meta: map[string]string{ + "test-key": "test-value", + }, })) } diff --git a/jobspec/parse_service.go b/jobspec/parse_service.go index 70e715af2..360382185 100644 --- a/jobspec/parse_service.go +++ b/jobspec/parse_service.go @@ -664,6 +664,7 @@ func parseSidecarService(o *ast.ObjectItem) (*api.ConsulSidecarService, error) { "proxy", "tags", "disable_default_tcp_check", + "meta", } if err := checkHCLKeys(o.Val, valid); err != nil { diff --git a/jobspec/parse_test.go b/jobspec/parse_test.go index f23c1f2eb..173b338ca 100644 --- a/jobspec/parse_test.go +++ b/jobspec/parse_test.go @@ -1346,6 +1346,31 @@ func TestParse(t *testing.T) { }, false, }, + { + "tg-service-connect-sidecar_meta.hcl", + &api.Job{ + ID: stringToPtr("sidecar_meta"), + Name: stringToPtr("sidecar_meta"), + Type: stringToPtr("service"), + TaskGroups: []*api.TaskGroup{{ + Name: stringToPtr("group"), + Services: []*api.Service{{ + Name: "example", + Connect: &api.ConsulConnect{ + Native: false, + SidecarService: &api.ConsulSidecarService{ + Meta: map[string]string{ + "test-key": "test-value", + "test-key1": "test-value1", + "test-key2": "test-value2", + }, + }, + }, + }}, + }}, + }, + false, + }, { "tg-service-connect-resources.hcl", &api.Job{ diff --git a/jobspec/test-fixtures/tg-service-connect-sidecar_meta.hcl b/jobspec/test-fixtures/tg-service-connect-sidecar_meta.hcl new file mode 100644 index 000000000..e12a7769f --- /dev/null +++ b/jobspec/test-fixtures/tg-service-connect-sidecar_meta.hcl @@ -0,0 +1,19 @@ +job "sidecar_meta" { + type = "service" + + group "group" { + service { + name = "example" + + connect { + sidecar_service { + meta { + test-key = "test-value" + test-key1 = "test-value1" + test-key2 = "test-value2" + } + } + } + } + } +} diff --git a/nomad/structs/diff_test.go b/nomad/structs/diff_test.go index 8cef345cd..0d25622ac 100644 --- a/nomad/structs/diff_test.go +++ b/nomad/structs/diff_test.go @@ -8260,6 +8260,84 @@ func TestServicesDiff(t *testing.T) { }, }, }, + }, { + Name: "SidecarService with different meta", + Contextual: false, + Old: []*Service{ + { + Name: "webapp", + Provider: "consul", + PortLabel: "http", + Connect: &ConsulConnect{ + SidecarService: &ConsulSidecarService{ + Port: "http", + Proxy: &ConsulProxy{}, + Meta: map[string]string{ + "foo": "qux", + }, + }, + Gateway: &ConsulGateway{ + Ingress: &ConsulIngressConfigEntry{}, + }, + }, + }, + }, + New: []*Service{ + { + Name: "webapp", + Provider: "consul", + PortLabel: "http", + Connect: &ConsulConnect{ + SidecarService: &ConsulSidecarService{ + Port: "http", + Proxy: &ConsulProxy{}, + Meta: map[string]string{ + "foo": "var", + "testKey": "testValue", + }, + }, + Gateway: &ConsulGateway{ + Ingress: &ConsulIngressConfigEntry{}, + }, + }, + }, + }, + Expected: []*ObjectDiff{ + { + Type: DiffTypeEdited, + Name: "Service", + Objects: []*ObjectDiff{ + { + Type: "Edited", + Name: "ConsulConnect", + Fields: nil, + Objects: []*ObjectDiff{ + { + Type: "Edited", + Name: "SidecarService", + Fields: []*FieldDiff{ + { + Type: "Edited", + Name: "Meta[foo]", + Old: "qux", + New: "var", + Annotations: nil, + }, + { + Type: "Added", + Name: "Meta[testKey]", + Old: "", + New: "testValue", + Annotations: nil, + }, + }, + Objects: nil, + }, + }, + }, + }, + }, + }, }, } diff --git a/nomad/structs/services.go b/nomad/structs/services.go index 4496ba707..972c2c199 100644 --- a/nomad/structs/services.go +++ b/nomad/structs/services.go @@ -1101,6 +1101,9 @@ type ConsulSidecarService struct { // DisableDefaultTCPCheck, if true, instructs Nomad to avoid setting a // default TCP check for the sidecar service. DisableDefaultTCPCheck bool + + // Meta specifies arbitrary KV metadata linked to the sidecar service. + Meta map[string]string } // HasUpstreams checks if the sidecar service has any upstreams configured @@ -1118,6 +1121,7 @@ func (s *ConsulSidecarService) Copy() *ConsulSidecarService { Port: s.Port, Proxy: s.Proxy.Copy(), DisableDefaultTCPCheck: s.DisableDefaultTCPCheck, + Meta: maps.Clone(s.Meta), } } @@ -1139,6 +1143,10 @@ func (s *ConsulSidecarService) Equal(o *ConsulSidecarService) bool { return false } + if !maps.Equal(s.Meta, o.Meta) { + return false + } + return s.Proxy.Equal(o.Proxy) } diff --git a/nomad/structs/services_test.go b/nomad/structs/services_test.go index bad1dd57a..b0e3d1d66 100644 --- a/nomad/structs/services_test.go +++ b/nomad/structs/services_test.go @@ -390,6 +390,9 @@ func TestService_Hash(t *testing.T) { Config: map[string]any{"foo": "bar"}, }}, }, + Meta: map[string]string{ + "test-key": "test-value", + }, }, // SidecarTask: nil // not hashed }} @@ -530,6 +533,9 @@ func TestConsulConnect_CopyEqual(t *testing.T) { "foo": 1, }, }, + Meta: map[string]string{ + "test-key": "test-value", + }, }, } @@ -833,12 +839,18 @@ func TestConsulSidecarService_Copy(t *testing.T) { Tags: []string{"foo", "bar"}, Port: "port1", Proxy: &ConsulProxy{LocalServiceAddress: "10.0.0.1"}, + Meta: map[string]string{ + "test-key": "test-value", + }, } result := s.Copy() require.Equal(t, &ConsulSidecarService{ Tags: []string{"foo", "bar"}, Port: "port1", Proxy: &ConsulProxy{LocalServiceAddress: "10.0.0.1"}, + Meta: map[string]string{ + "test-key": "test-value", + }, }, result) }) } diff --git a/website/content/docs/job-specification/sidecar_service.mdx b/website/content/docs/job-specification/sidecar_service.mdx index 8197da302..584ef6cf0 100644 --- a/website/content/docs/job-specification/sidecar_service.mdx +++ b/website/content/docs/job-specification/sidecar_service.mdx @@ -49,6 +49,8 @@ job "countdash" { - `disable_default_tcp_check` `(bool: false)` - disable the default TCP health check. +- `meta` (map<string|string>: nil) - Specifies arbitrary KV metadata pairs. + - `port` `(string: )` - Port label for sidecar service. - `proxy` ([proxy][]: nil) - This is used to configure the @@ -67,7 +69,7 @@ The following example is a minimal `sidecar_service` block with defaults } ``` -The following example includes specifying upstreams. +The following example includes specifying upstreams and meta. ```hcl sidecar_service { @@ -77,6 +79,9 @@ The following example includes specifying upstreams. local_bind_port = 8080 } } + meta { + "test-key" = "test-value" + } } ```