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 },