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"
+ }
}
```