Merge pull request #4047 from pierresouchay/added_missing_meta_in_service_definition
[BUGFIX] Added Service Meta support in configuration files
This commit is contained in:
commit
ea731031d5
|
@ -155,9 +155,18 @@ func (s *HTTPServer) AgentServices(resp http.ResponseWriter, req *http.Request)
|
|||
|
||||
// Use empty list instead of nil
|
||||
for id, s := range services {
|
||||
if s.Tags == nil {
|
||||
if s.Tags == nil || s.Meta == nil {
|
||||
clone := *s
|
||||
clone.Tags = make([]string, 0)
|
||||
if s.Tags == nil {
|
||||
clone.Tags = make([]string, 0)
|
||||
} else {
|
||||
clone.Tags = s.Tags
|
||||
}
|
||||
if s.Meta == nil {
|
||||
clone.Meta = make(map[string]string)
|
||||
} else {
|
||||
clone.Meta = s.Meta
|
||||
}
|
||||
services[id] = &clone
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1204,6 +1204,7 @@ func TestAgent_RegisterService(t *testing.T) {
|
|||
|
||||
args := &structs.ServiceDefinition{
|
||||
Name: "test",
|
||||
Meta: map[string]string{"hello": "world"},
|
||||
Tags: []string{"master"},
|
||||
Port: 8000,
|
||||
Check: structs.CheckType{
|
||||
|
@ -1232,6 +1233,9 @@ func TestAgent_RegisterService(t *testing.T) {
|
|||
if _, ok := a.State.Services()["test"]; !ok {
|
||||
t.Fatalf("missing test service")
|
||||
}
|
||||
if val := a.State.Service("test").Meta["hello"]; val != "world" {
|
||||
t.Fatalf("Missing meta: %v", a.State.Service("test").Meta)
|
||||
}
|
||||
|
||||
// Ensure we have a check mapping
|
||||
checks := a.State.Checks()
|
||||
|
@ -1254,7 +1258,7 @@ func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
|
|||
a := NewTestAgent(t.Name(), "")
|
||||
defer a.Shutdown()
|
||||
|
||||
json := `{"name":"test", "port":8000, "enable_tag_override": true}`
|
||||
json := `{"name":"test", "port":8000, "enable_tag_override": true, "meta": {"some": "meta"}}`
|
||||
req, _ := http.NewRequest("PUT", "/v1/agent/service/register", strings.NewReader(json))
|
||||
|
||||
obj, err := a.srv.AgentRegisterService(nil, req)
|
||||
|
@ -1264,10 +1268,10 @@ func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
|
|||
if obj != nil {
|
||||
t.Fatalf("bad: %v", obj)
|
||||
}
|
||||
|
||||
svc := &structs.NodeService{
|
||||
ID: "test",
|
||||
Service: "test",
|
||||
Meta: map[string]string{"some": "meta"},
|
||||
Port: 8000,
|
||||
EnableTagOverride: true,
|
||||
}
|
||||
|
|
|
@ -998,11 +998,18 @@ func (b *Builder) serviceVal(v *ServiceDefinition) *structs.ServiceDefinition {
|
|||
checks = append(checks, b.checkVal(v.Check).CheckType())
|
||||
}
|
||||
|
||||
meta := make(map[string]string)
|
||||
if err := structs.ValidateMetadata(v.Meta, false); err != nil {
|
||||
b.err = multierror.Append(fmt.Errorf("invalid meta for service %s: %v", b.stringVal(v.Name), err))
|
||||
} else {
|
||||
meta = v.Meta
|
||||
}
|
||||
return &structs.ServiceDefinition{
|
||||
ID: b.stringVal(v.ID),
|
||||
Name: b.stringVal(v.Name),
|
||||
Tags: v.Tags,
|
||||
Address: b.stringVal(v.Address),
|
||||
Meta: meta,
|
||||
Port: b.intVal(v.Port),
|
||||
Token: b.stringVal(v.Token),
|
||||
EnableTagOverride: b.boolVal(v.EnableTagOverride),
|
||||
|
|
|
@ -319,6 +319,7 @@ type ServiceDefinition struct {
|
|||
Name *string `json:"name,omitempty" hcl:"name" mapstructure:"name"`
|
||||
Tags []string `json:"tags,omitempty" hcl:"tags" mapstructure:"tags"`
|
||||
Address *string `json:"address,omitempty" hcl:"address" mapstructure:"address"`
|
||||
Meta map[string]string `json:"meta,omitempty" hcl:"meta" mapstructure:"meta"`
|
||||
Port *int `json:"port,omitempty" hcl:"port" mapstructure:"port"`
|
||||
Check *CheckDefinition `json:"check,omitempty" hcl:"check" mapstructure:"check"`
|
||||
Checks []CheckDefinition `json:"checks,omitempty" hcl:"checks" mapstructure:"checks"`
|
||||
|
|
|
@ -1923,20 +1923,59 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
|
|||
},
|
||||
json: []string{
|
||||
`{ "service": { "name": "a", "port": 80 } }`,
|
||||
`{ "service": { "name": "b", "port": 90 } }`,
|
||||
`{ "service": { "name": "b", "port": 90, "meta": {"my": "value"} } }`,
|
||||
},
|
||||
hcl: []string{
|
||||
`service = { name = "a" port = 80 }`,
|
||||
`service = { name = "b" port = 90 }`,
|
||||
`service = { name = "b" port = 90 meta={my="value"}}`,
|
||||
},
|
||||
patch: func(rt *RuntimeConfig) {
|
||||
rt.Services = []*structs.ServiceDefinition{
|
||||
&structs.ServiceDefinition{Name: "a", Port: 80},
|
||||
&structs.ServiceDefinition{Name: "b", Port: 90},
|
||||
&structs.ServiceDefinition{Name: "b", Port: 90, Meta: map[string]string{"my": "value"}},
|
||||
}
|
||||
rt.DataDir = dataDir
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "service with wrong meta: too long key",
|
||||
args: []string{
|
||||
`-data-dir=` + dataDir,
|
||||
},
|
||||
json: []string{
|
||||
`{ "service": { "name": "a", "port": 80, "meta": { "` + randomString(520) + `": "metaValue" } } }`,
|
||||
},
|
||||
hcl: []string{
|
||||
`service = { name = "a" port = 80, meta={` + randomString(520) + `="metaValue"} }`,
|
||||
},
|
||||
err: `Key is too long`,
|
||||
},
|
||||
{
|
||||
desc: "service with wrong meta: too long value",
|
||||
args: []string{
|
||||
`-data-dir=` + dataDir,
|
||||
},
|
||||
json: []string{
|
||||
`{ "service": { "name": "a", "port": 80, "meta": { "a": "` + randomString(520) + `" } } }`,
|
||||
},
|
||||
hcl: []string{
|
||||
`service = { name = "a" port = 80, meta={a="` + randomString(520) + `"} }`,
|
||||
},
|
||||
err: `Value is too long`,
|
||||
},
|
||||
{
|
||||
desc: "service with wrong meta: too many meta",
|
||||
args: []string{
|
||||
`-data-dir=` + dataDir,
|
||||
},
|
||||
json: []string{
|
||||
`{ "service": { "name": "a", "port": 80, "meta": { ` + metaPairs(70, "json") + `} } }`,
|
||||
},
|
||||
hcl: []string{
|
||||
`service = { name = "a" port = 80 meta={` + metaPairs(70, "hcl") + `} }`,
|
||||
},
|
||||
err: `invalid meta for service a: Node metadata cannot contain more than 64 key`,
|
||||
},
|
||||
{
|
||||
desc: "translated keys",
|
||||
args: []string{
|
||||
|
@ -2397,6 +2436,9 @@ func TestFullConfig(t *testing.T) {
|
|||
"service": {
|
||||
"id": "dLOXpSCI",
|
||||
"name": "o1ynPkp0",
|
||||
"meta": {
|
||||
"mymeta": "data"
|
||||
},
|
||||
"tags": ["nkwshvM5", "NTDWn3ek"],
|
||||
"address": "cOlSOhbp",
|
||||
"token": "msy7iWER",
|
||||
|
@ -2835,6 +2877,9 @@ func TestFullConfig(t *testing.T) {
|
|||
service = {
|
||||
id = "dLOXpSCI"
|
||||
name = "o1ynPkp0"
|
||||
meta = {
|
||||
mymeta = "data"
|
||||
}
|
||||
tags = ["nkwshvM5", "NTDWn3ek"]
|
||||
address = "cOlSOhbp"
|
||||
token = "msy7iWER"
|
||||
|
@ -3489,6 +3534,7 @@ func TestFullConfig(t *testing.T) {
|
|||
Tags: []string{"nkwshvM5", "NTDWn3ek"},
|
||||
Address: "cOlSOhbp",
|
||||
Token: "msy7iWER",
|
||||
Meta: map[string]string{"mymeta": "data"},
|
||||
Port: 24237,
|
||||
EnableTagOverride: true,
|
||||
Checks: structs.CheckTypes{
|
||||
|
|
|
@ -678,6 +678,7 @@ func vetRegisterWithACL(rule acl.ACL, subj *structs.RegisterRequest,
|
|||
ID: subj.Service.ID,
|
||||
Service: subj.Service.Service,
|
||||
Tags: subj.Service.Tags,
|
||||
Meta: subj.Service.Meta,
|
||||
Address: subj.Service.Address,
|
||||
Port: subj.Service.Port,
|
||||
EnableTagOverride: subj.Service.EnableTagOverride,
|
||||
|
|
|
@ -23,6 +23,7 @@ type AgentService struct {
|
|||
ID string
|
||||
Service string
|
||||
Tags []string
|
||||
Meta map[string]string
|
||||
Port int
|
||||
Address string
|
||||
EnableTagOverride bool
|
||||
|
|
|
@ -73,7 +73,7 @@ func TestAPI_AgentReload(t *testing.T) {
|
|||
agent := c.Agent()
|
||||
|
||||
// Update the config file with a service definition
|
||||
config := `{"service":{"name":"redis", "port":1234}}`
|
||||
config := `{"service":{"name":"redis", "port":1234, "Meta": {"some": "meta"}}}`
|
||||
err = ioutil.WriteFile(configFile.Name(), []byte(config), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
|
@ -95,6 +95,9 @@ func TestAPI_AgentReload(t *testing.T) {
|
|||
if service.Port != 1234 {
|
||||
t.Fatalf("bad: %v", service.Port)
|
||||
}
|
||||
if service.Meta["some"] != "meta" {
|
||||
t.Fatalf("Missing metadata some:=meta in %v", service)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPI_AgentMembersOpts(t *testing.T) {
|
||||
|
|
|
@ -25,6 +25,9 @@ A service definition is a script that looks like:
|
|||
"name": "redis",
|
||||
"tags": ["primary"],
|
||||
"address": "",
|
||||
"meta": {
|
||||
"meta": "for my service"
|
||||
}
|
||||
"port": 8000,
|
||||
"enable_tag_override": false,
|
||||
"checks": [
|
||||
|
@ -38,8 +41,8 @@ A service definition is a script that looks like:
|
|||
```
|
||||
|
||||
A service definition must include a `name` and may optionally provide an
|
||||
`id`, `tags`, `address`, `port`, `check`, and `enable_tag_override`. The
|
||||
`id` is set to the `name` if not provided. It is required that all
|
||||
`id`, `tags`, `address`, `port`, `check`, `meta` and `enable_tag_override`.
|
||||
The `id` is set to the `name` if not provided. It is required that all
|
||||
services have a unique ID per node, so if names might conflict then
|
||||
unique IDs should be provided.
|
||||
|
||||
|
@ -57,6 +60,14 @@ The `port` field can be used as well to make a service-oriented architecture
|
|||
simpler to configure; this way, the address and port of a service can
|
||||
be discovered.
|
||||
|
||||
The `meta` object is a map of max 64 key/values with string semantics. Key can contain
|
||||
only ASCII chars and no special characters (`A-Z` `a-z` `0-9` `_` and `-`).
|
||||
For performance and security reasons, values as well as keys are limited to 128
|
||||
characters for keys, 512 for values. This object has the same limitations as the node
|
||||
meta object in node definition.
|
||||
All those meta data can be retrieved individually per instance of the service
|
||||
and all the instances of a given service have their own copy of it.
|
||||
|
||||
Services may also contain a `token` field to provide an ACL token. This token is
|
||||
used for any interaction with the catalog for the service, including
|
||||
[anti-entropy syncs](/docs/internals/anti-entropy.html) and deregistration.
|
||||
|
|
Loading…
Reference in New Issue