mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 10:25:42 +03:00
Consul service meta (#6193)
* adds meta object to service in job spec, sends it to consul * adds tests for service meta * fix tests * adds docs * better hashing for service meta, use helper for copying meta when registering service * tried to be DRY, but looks like it would be more work to use the helper function
This commit is contained in:
committed by
Nick Ethier
parent
974ff0392c
commit
25e38c8257
@@ -106,6 +106,7 @@ type Service struct {
|
||||
Checks []ServiceCheck
|
||||
CheckRestart *CheckRestart `mapstructure:"check_restart"`
|
||||
Connect *ConsulConnect
|
||||
Meta map[string]string
|
||||
}
|
||||
|
||||
// Canonicalize the Service by ensuring its name and address mode are set. Task
|
||||
|
||||
@@ -252,6 +252,15 @@ func interpolateServices(taskEnv *taskenv.TaskEnv, services []*structs.Service)
|
||||
service.PortLabel = taskEnv.ReplaceEnv(service.PortLabel)
|
||||
service.Tags = taskEnv.ParseAndReplace(service.Tags)
|
||||
service.CanaryTags = taskEnv.ParseAndReplace(service.CanaryTags)
|
||||
|
||||
if len(service.Meta) > 0 {
|
||||
meta := make(map[string]string, len(service.Meta))
|
||||
for k, v := range service.Meta {
|
||||
meta[k] = taskEnv.ReplaceEnv(v)
|
||||
}
|
||||
service.Meta = meta
|
||||
}
|
||||
|
||||
interpolated[i] = service
|
||||
}
|
||||
|
||||
|
||||
@@ -728,6 +728,14 @@ func (c *ServiceClient) serviceRegs(ops *operations, service *structs.Service, t
|
||||
return nil, fmt.Errorf("invalid Consul Connect configuration for service %q: %v", service.Name, err)
|
||||
}
|
||||
|
||||
meta := make(map[string]string, len(service.Meta))
|
||||
for k, v := range service.Meta {
|
||||
meta[k] = v
|
||||
}
|
||||
|
||||
// This enables the consul UI to show that Nomad registered this service
|
||||
meta["external-source"] = "nomad"
|
||||
|
||||
// Build the Consul Service registration request
|
||||
serviceReg := &api.AgentServiceRegistration{
|
||||
ID: id,
|
||||
@@ -735,10 +743,7 @@ func (c *ServiceClient) serviceRegs(ops *operations, service *structs.Service, t
|
||||
Tags: tags,
|
||||
Address: ip,
|
||||
Port: port,
|
||||
// This enables the consul UI to show that Nomad registered this service
|
||||
Meta: map[string]string{
|
||||
"external-source": "nomad",
|
||||
},
|
||||
Meta: meta,
|
||||
Connect: connect, // will be nil if no Connect stanza
|
||||
}
|
||||
ops.regServices = append(ops.regServices, serviceReg)
|
||||
|
||||
@@ -828,6 +828,7 @@ func ApiTaskToStructsTask(apiTask *api.Task, structsTask *structs.Task) {
|
||||
Tags: service.Tags,
|
||||
CanaryTags: service.CanaryTags,
|
||||
AddressMode: service.AddressMode,
|
||||
Meta: helper.CopyMapStringString(service.Meta),
|
||||
}
|
||||
|
||||
if l := len(service.Checks); l != 0 {
|
||||
@@ -1005,6 +1006,7 @@ func ApiServicesToStructs(in []*api.Service) []*structs.Service {
|
||||
Tags: s.Tags,
|
||||
CanaryTags: s.CanaryTags,
|
||||
AddressMode: s.AddressMode,
|
||||
Meta: helper.CopyMapStringString(s.Meta),
|
||||
}
|
||||
|
||||
if l := len(s.Checks); l != 0 {
|
||||
|
||||
@@ -1507,6 +1507,9 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
||||
Tags: []string{"a", "b"},
|
||||
CanaryTags: []string{"d", "e"},
|
||||
PortLabel: "1234",
|
||||
Meta: map[string]string{
|
||||
"servicemeta": "foobar",
|
||||
},
|
||||
CheckRestart: &api.CheckRestart{
|
||||
Limit: 4,
|
||||
Grace: helper.TimeToPtr(11 * time.Second),
|
||||
@@ -1571,6 +1574,9 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
||||
Tags: []string{"1", "2"},
|
||||
CanaryTags: []string{"3", "4"},
|
||||
PortLabel: "foo",
|
||||
Meta: map[string]string{
|
||||
"servicemeta": "foobar",
|
||||
},
|
||||
CheckRestart: &api.CheckRestart{
|
||||
Limit: 4,
|
||||
Grace: helper.TimeToPtr(11 * time.Second),
|
||||
@@ -1845,6 +1851,9 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
||||
CanaryTags: []string{"d", "e"},
|
||||
PortLabel: "1234",
|
||||
AddressMode: "auto",
|
||||
Meta: map[string]string{
|
||||
"servicemeta": "foobar",
|
||||
},
|
||||
Checks: []*structs.ServiceCheck{
|
||||
{
|
||||
Name: "bar",
|
||||
@@ -1904,6 +1913,9 @@ func TestJobs_ApiJobToStructsJob(t *testing.T) {
|
||||
CanaryTags: []string{"3", "4"},
|
||||
PortLabel: "foo",
|
||||
AddressMode: "auto",
|
||||
Meta: map[string]string{
|
||||
"servicemeta": "foobar",
|
||||
},
|
||||
Checks: []*structs.ServiceCheck{
|
||||
{
|
||||
Name: "bar",
|
||||
|
||||
@@ -46,6 +46,7 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) {
|
||||
"address_mode",
|
||||
"check_restart",
|
||||
"connect",
|
||||
"meta",
|
||||
}
|
||||
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
|
||||
return nil, err
|
||||
@@ -60,27 +61,28 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) {
|
||||
delete(m, "check")
|
||||
delete(m, "check_restart")
|
||||
delete(m, "connect")
|
||||
delete(m, "meta")
|
||||
|
||||
if err := mapstructure.WeakDecode(m, &service); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Filter checks
|
||||
var checkList *ast.ObjectList
|
||||
// Filter list
|
||||
var listVal *ast.ObjectList
|
||||
if ot, ok := o.Val.(*ast.ObjectType); ok {
|
||||
checkList = ot.List
|
||||
listVal = ot.List
|
||||
} else {
|
||||
return nil, fmt.Errorf("'%s': should be an object", service.Name)
|
||||
}
|
||||
|
||||
if co := checkList.Filter("check"); len(co.Items) > 0 {
|
||||
if co := listVal.Filter("check"); len(co.Items) > 0 {
|
||||
if err := parseChecks(&service, co); err != nil {
|
||||
return nil, multierror.Prefix(err, fmt.Sprintf("'%s',", service.Name))
|
||||
}
|
||||
}
|
||||
|
||||
// Filter check_restart
|
||||
if cro := checkList.Filter("check_restart"); len(cro.Items) > 0 {
|
||||
if cro := listVal.Filter("check_restart"); len(cro.Items) > 0 {
|
||||
if len(cro.Items) > 1 {
|
||||
return nil, fmt.Errorf("check_restart '%s': cannot have more than 1 check_restart", service.Name)
|
||||
}
|
||||
@@ -93,7 +95,7 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) {
|
||||
}
|
||||
|
||||
// Filter connect
|
||||
if co := checkList.Filter("connect"); len(co.Items) > 0 {
|
||||
if co := listVal.Filter("connect"); len(co.Items) > 0 {
|
||||
if len(co.Items) > 1 {
|
||||
return nil, fmt.Errorf("connect '%s': cannot have more than 1 connect stanza", service.Name)
|
||||
}
|
||||
@@ -106,6 +108,20 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) {
|
||||
service.Connect = c
|
||||
}
|
||||
|
||||
// Parse out meta fields. These are in HCL as a list so we need
|
||||
// to iterate over them and merge them.
|
||||
if metaO := listVal.Filter("meta"); len(metaO.Items) > 0 {
|
||||
for _, o := range metaO.Elem().Items {
|
||||
var m map[string]interface{}
|
||||
if err := hcl.DecodeObject(&m, o.Val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := mapstructure.WeakDecode(m, &service.Meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &service, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -784,6 +784,33 @@ func TestParse(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"service-meta.hcl",
|
||||
&api.Job{
|
||||
ID: helper.StringToPtr("service_meta"),
|
||||
Name: helper.StringToPtr("service_meta"),
|
||||
Type: helper.StringToPtr("service"),
|
||||
TaskGroups: []*api.TaskGroup{
|
||||
{
|
||||
Name: helper.StringToPtr("group"),
|
||||
Tasks: []*api.Task{
|
||||
{
|
||||
Name: "task",
|
||||
Services: []*api.Service{
|
||||
{
|
||||
Name: "http-service",
|
||||
Meta: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"reschedule-job.hcl",
|
||||
&api.Job{
|
||||
|
||||
14
jobspec/test-fixtures/service-meta.hcl
Normal file
14
jobspec/test-fixtures/service-meta.hcl
Normal file
@@ -0,0 +1,14 @@
|
||||
job "service_meta" {
|
||||
type = "service"
|
||||
group "group" {
|
||||
task "task" {
|
||||
service {
|
||||
name = "http-service"
|
||||
meta {
|
||||
foo = "bar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,10 +324,11 @@ type Service struct {
|
||||
// this service.
|
||||
AddressMode string
|
||||
|
||||
Tags []string // List of tags for the service
|
||||
CanaryTags []string // List of tags for the service when it is a canary
|
||||
Checks []*ServiceCheck // List of checks associated with the service
|
||||
Connect *ConsulConnect // Consul Connect configuration
|
||||
Tags []string // List of tags for the service
|
||||
CanaryTags []string // List of tags for the service when it is a canary
|
||||
Checks []*ServiceCheck // List of checks associated with the service
|
||||
Connect *ConsulConnect // Consul Connect configuration
|
||||
Meta map[string]string // Consul service meta
|
||||
}
|
||||
|
||||
// Copy the stanza recursively. Returns nil if nil.
|
||||
@@ -350,6 +351,8 @@ func (s *Service) Copy() *Service {
|
||||
|
||||
ns.Connect = s.Connect.Copy()
|
||||
|
||||
ns.Meta = helper.CopyMapStringString(s.Meta)
|
||||
|
||||
return ns
|
||||
}
|
||||
|
||||
@@ -458,6 +461,9 @@ func (s *Service) Hash(allocID, taskName string, canary bool) string {
|
||||
for _, tag := range s.CanaryTags {
|
||||
io.WriteString(h, tag)
|
||||
}
|
||||
if len(s.Meta) > 0 {
|
||||
fmt.Fprintf(h, "%v", s.Meta)
|
||||
}
|
||||
|
||||
// Vary ID on whether or not CanaryTags will be used
|
||||
if canary {
|
||||
@@ -514,6 +520,10 @@ OUTER:
|
||||
return false
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(s.Meta, o.Meta) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !helper.CompareSliceSetString(s.Tags, o.Tags) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -146,6 +146,9 @@ The table below shows this endpoint's support for
|
||||
"global",
|
||||
"cache"
|
||||
],
|
||||
"Meta": {
|
||||
"meta": "for my service"
|
||||
},
|
||||
"PortLabel": "db",
|
||||
"AddressMode": "",
|
||||
"Checks": [{
|
||||
|
||||
@@ -56,6 +56,9 @@ Below is the JSON representation of the job outputted by `$ nomad init`:
|
||||
"global",
|
||||
"cache"
|
||||
],
|
||||
"Meta": {
|
||||
"meta": "for my service",
|
||||
},
|
||||
"PortLabel": "db",
|
||||
"AddressMode": "",
|
||||
"Checks": [{
|
||||
@@ -400,6 +403,9 @@ The `Task` object supports the following keys:
|
||||
|
||||
- `Tags`: A list of string tags associated with this Service. String
|
||||
interpolation is supported in tags.
|
||||
|
||||
- `Meta`: A key-value map that annotates the Consul service with
|
||||
user-defined metadata. String interpolation is supported in meta.
|
||||
|
||||
- `CanaryTags`: A list of string tags associated with this Service while it
|
||||
is a canary. Once the canary is promoted, the registered tags will be
|
||||
|
||||
@@ -33,6 +33,10 @@ job "docs" {
|
||||
|
||||
port = "db"
|
||||
|
||||
meta {
|
||||
meta = "for your service"
|
||||
}
|
||||
|
||||
check {
|
||||
type = "tcp"
|
||||
port = "db"
|
||||
@@ -135,6 +139,9 @@ does not automatically enable service discovery.
|
||||
implemented for Docker and rkt.
|
||||
|
||||
- `host` - Use the host IP and port.
|
||||
|
||||
- `meta` <code>([Meta][]: nil)</code> - Specifies a key-value map that annotates
|
||||
the Consul service with user-defined metadata.
|
||||
|
||||
### `check` Parameters
|
||||
|
||||
|
||||
Reference in New Issue
Block a user