From d2ab3deacfb0aa1983fd04c6cee05e354be28580 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Wed, 18 Apr 2018 22:18:58 +0200
Subject: [PATCH 01/10] =?UTF-8?q?[BUGFIX]=C2=A0Added=20Service=20Meta=20su?=
=?UTF-8?q?pport=20in=20configuration=20files?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes https://github.com/hashicorp/consul/issues/4045
Was not added by mistake in https://github.com/hashicorp/consul/pull/3881
---
agent/config/builder.go | 7 +++++++
agent/config/config.go | 1 +
agent/config/runtime_test.go | 8 +++++---
3 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/agent/config/builder.go b/agent/config/builder.go
index e9cb19394c..7b99b49602 100644
--- a/agent/config/builder.go
+++ b/agent/config/builder.go
@@ -997,11 +997,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 %v: %v", 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),
diff --git a/agent/config/config.go b/agent/config/config.go
index 2a14abc6b8..6b8ba113a5 100644
--- a/agent/config/config.go
+++ b/agent/config/config.go
@@ -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:"node_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"`
diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go
index da62e0339c..511ebc4a80 100644
--- a/agent/config/runtime_test.go
+++ b/agent/config/runtime_test.go
@@ -49,6 +49,8 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
dataDir := testutil.TempDir(t, "consul")
defer os.RemoveAll(dataDir)
+ metaVal := make(map[string]string)
+ metaVal["my"] = "value"
tests := []configTest{
// ------------------------------------------------------------
// cmd line flags
@@ -1923,16 +1925,16 @@ 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: metaVal},
}
rt.DataDir = dataDir
},
From 2f5e67534d0b52540122bdc33c9c6d6077e3e5bf Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Wed, 18 Apr 2018 22:57:33 +0200
Subject: [PATCH 02/10] Added unit tests for bad meta values
---
agent/config/builder.go | 2 +-
agent/config/runtime_test.go | 26 ++++++++++++++++++++++++++
2 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/agent/config/builder.go b/agent/config/builder.go
index 7b99b49602..d0a3a353de 100644
--- a/agent/config/builder.go
+++ b/agent/config/builder.go
@@ -999,7 +999,7 @@ func (b *Builder) serviceVal(v *ServiceDefinition) *structs.ServiceDefinition {
meta := make(map[string]string)
if err := structs.ValidateMetadata(v.Meta, false); err != nil {
- b.err = multierror.Append(fmt.Errorf("invalid meta for service %v: %v", v.Name, err))
+ b.err = multierror.Append(fmt.Errorf("invalid meta for service %s: %v", *v.Name, err))
} else {
meta = v.Meta
}
diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go
index 511ebc4a80..4306ce6ad9 100644
--- a/agent/config/runtime_test.go
+++ b/agent/config/runtime_test.go
@@ -1939,6 +1939,32 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
rt.DataDir = dataDir
},
},
+ {
+ desc: "service with wrong meta: too long",
+ 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{
From 9bb15730a667e0c7c27492b1e62be09fffa80132 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Wed, 18 Apr 2018 23:07:25 +0200
Subject: [PATCH 03/10] Added unit test on key length
---
agent/config/runtime_test.go | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go
index 4306ce6ad9..653490d258 100644
--- a/agent/config/runtime_test.go
+++ b/agent/config/runtime_test.go
@@ -1940,7 +1940,20 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
},
},
{
- desc: "service with wrong meta: too long",
+ 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,
},
From 06a181955de9bf70096ae5cf2ae1f90ff39ba5f6 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Wed, 18 Apr 2018 23:18:16 +0200
Subject: [PATCH 04/10] Use safer stringVal()
---
agent/config/builder.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/agent/config/builder.go b/agent/config/builder.go
index d0a3a353de..c3fa42d1a4 100644
--- a/agent/config/builder.go
+++ b/agent/config/builder.go
@@ -999,7 +999,7 @@ func (b *Builder) serviceVal(v *ServiceDefinition) *structs.ServiceDefinition {
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", *v.Name, err))
+ b.err = multierror.Append(fmt.Errorf("invalid meta for service %s: %v", b.stringVal(v.Name), err))
} else {
meta = v.Meta
}
From c152cb7bdf685469a4ec70c75e230d9305f94f8c Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Sat, 21 Apr 2018 17:34:29 +0200
Subject: [PATCH 05/10] Added Missing Service Meta synchronization and field
---
agent/agent_endpoint.go | 13 +++++++++++--
agent/agent_endpoint_test.go | 8 ++++++--
agent/consul/acl.go | 1 +
api/agent.go | 1 +
api/agent_test.go | 5 ++++-
5 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/agent/agent_endpoint.go b/agent/agent_endpoint.go
index 0e2b4b4803..0fa02b9738 100644
--- a/agent/agent_endpoint.go
+++ b/agent/agent_endpoint.go
@@ -131,9 +131,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
}
}
diff --git a/agent/agent_endpoint_test.go b/agent/agent_endpoint_test.go
index 940304b548..c6e4f05324 100644
--- a/agent/agent_endpoint_test.go
+++ b/agent/agent_endpoint_test.go
@@ -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,
}
diff --git a/agent/consul/acl.go b/agent/consul/acl.go
index bea2a5617d..eb14e1dabc 100644
--- a/agent/consul/acl.go
+++ b/agent/consul/acl.go
@@ -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,
diff --git a/api/agent.go b/api/agent.go
index b42baed41d..772948ad1e 100644
--- a/api/agent.go
+++ b/api/agent.go
@@ -23,6 +23,7 @@ type AgentService struct {
ID string
Service string
Tags []string
+ Meta map[string]string
Port int
Address string
EnableTagOverride bool
diff --git a/api/agent_test.go b/api/agent_test.go
index e5ccdb0ef2..a05c4faf1a 100644
--- a/api/agent_test.go
+++ b/api/agent_test.go
@@ -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) {
From eccc223480fdbcf867015bf144cb47dede2fc924 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Tue, 24 Apr 2018 16:39:43 +0200
Subject: [PATCH 06/10] Fixed Meta name for JSON + Added unit tests for
HCL/JSON
---
agent/config/config.go | 2 +-
agent/config/runtime_test.go | 7 +++++++
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/agent/config/config.go b/agent/config/config.go
index 6b8ba113a5..7ae14fcdd0 100644
--- a/agent/config/config.go
+++ b/agent/config/config.go
@@ -319,7 +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:"node_meta,omitempty" hcl:"meta" mapstructure:"meta"`
+ 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"`
diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go
index 653490d258..f13bc77737 100644
--- a/agent/config/runtime_test.go
+++ b/agent/config/runtime_test.go
@@ -2438,6 +2438,9 @@ func TestFullConfig(t *testing.T) {
"service": {
"id": "dLOXpSCI",
"name": "o1ynPkp0",
+ "meta": {
+ "mymeta": "data"
+ },
"tags": ["nkwshvM5", "NTDWn3ek"],
"address": "cOlSOhbp",
"token": "msy7iWER",
@@ -2875,6 +2878,9 @@ func TestFullConfig(t *testing.T) {
service = {
id = "dLOXpSCI"
name = "o1ynPkp0"
+ meta = {
+ mymeta = "data"
+ }
tags = ["nkwshvM5", "NTDWn3ek"]
address = "cOlSOhbp"
token = "msy7iWER"
@@ -3528,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{
From 4c1ddbd0d52655dff2e9a1dadc2517ecdbdc17ce Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Tue, 24 Apr 2018 21:56:35 +0200
Subject: [PATCH 07/10] Added documentation for meta
---
website/source/docs/agent/services.html.md | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/website/source/docs/agent/services.html.md b/website/source/docs/agent/services.html.md
index f8875e2bc1..746eff388b 100644
--- a/website/source/docs/agent/services.html.md
+++ b/website/source/docs/agent/services.html.md
@@ -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,13 @@ 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 `-`, no `.`).
+For performance and security reasons, values as well as keys are limited to 128
+characters for keys, 512 for values, it has the same limitations as the node meta
+keys. 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.
From 7e0d6f57e990315c98d5f53c6fee58814dfcfb3c Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Tue, 24 Apr 2018 22:40:41 +0200
Subject: [PATCH 08/10] Improved documentation
---
website/source/docs/agent/services.html.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/website/source/docs/agent/services.html.md b/website/source/docs/agent/services.html.md
index 746eff388b..81a72edd10 100644
--- a/website/source/docs/agent/services.html.md
+++ b/website/source/docs/agent/services.html.md
@@ -61,10 +61,11 @@ 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 `-`, no `.`).
+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, it has the same limitations as the node meta
-keys. All those meta data can be retrieved individually per instance of the service
+characters for keys, 512 for values. This object has the same limitations as the node
+meta keys.
+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
From 208566d541b6b0632bede7086f3a7cb1b739ba01 Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Tue, 24 Apr 2018 22:55:34 +0200
Subject: [PATCH 09/10] Changed a bit doc
(Fighting with Travis)
---
website/source/docs/agent/services.html.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/website/source/docs/agent/services.html.md b/website/source/docs/agent/services.html.md
index 81a72edd10..d01b85a0bf 100644
--- a/website/source/docs/agent/services.html.md
+++ b/website/source/docs/agent/services.html.md
@@ -64,7 +64,7 @@ The `meta` object is a map of max 64 key/values with string semantics. Key can c
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 keys.
+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.
From 303997ff552d4c70ebf66e0e1346bfe6f4e2a95d Mon Sep 17 00:00:00 2001
From: Pierre Souchay
Date: Tue, 24 Apr 2018 23:15:27 +0200
Subject: [PATCH 10/10] Improved unit test (example close to actual value)
---
agent/config/runtime_test.go | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/agent/config/runtime_test.go b/agent/config/runtime_test.go
index f13bc77737..d48d288163 100644
--- a/agent/config/runtime_test.go
+++ b/agent/config/runtime_test.go
@@ -49,8 +49,6 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
dataDir := testutil.TempDir(t, "consul")
defer os.RemoveAll(dataDir)
- metaVal := make(map[string]string)
- metaVal["my"] = "value"
tests := []configTest{
// ------------------------------------------------------------
// cmd line flags
@@ -1934,7 +1932,7 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
patch: func(rt *RuntimeConfig) {
rt.Services = []*structs.ServiceDefinition{
&structs.ServiceDefinition{Name: "a", Port: 80},
- &structs.ServiceDefinition{Name: "b", Port: 90, Meta: metaVal},
+ &structs.ServiceDefinition{Name: "b", Port: 90, Meta: map[string]string{"my": "value"}},
}
rt.DataDir = dataDir
},