diff --git a/api/api.go b/api/api.go
index 9587043a4..9a59b724c 100644
--- a/api/api.go
+++ b/api/api.go
@@ -74,6 +74,11 @@ type QueryOptions struct {
// that node. Setting this to "_agent" will use the agent's node
// for the sort.
Near string
+
+ // NodeMeta is used to filter results by nodes with the given
+ // metadata key/value pairs. Currently, only one key/value pair can
+ // be provided for filtering.
+ NodeMeta map[string]string
}
// WriteOptions are used to parameterize a write
@@ -386,6 +391,11 @@ func (r *request) setQueryOptions(q *QueryOptions) {
if q.Near != "" {
r.params.Set("near", q.Near)
}
+ if len(q.NodeMeta) > 0 {
+ for key, value := range q.NodeMeta {
+ r.params.Add("node-meta", key+":"+value)
+ }
+ }
}
// durToMsec converts a duration to a millisecond specified string. If the
diff --git a/api/catalog.go b/api/catalog.go
index 56f0dbf69..10e93b42d 100644
--- a/api/catalog.go
+++ b/api/catalog.go
@@ -4,12 +4,14 @@ type Node struct {
Node string
Address string
TaggedAddresses map[string]string
+ Meta map[string]string
}
type CatalogService struct {
Node string
Address string
TaggedAddresses map[string]string
+ NodeMeta map[string]string
ServiceID string
ServiceName string
ServiceAddress string
@@ -29,6 +31,7 @@ type CatalogRegistration struct {
Node string
Address string
TaggedAddresses map[string]string
+ NodeMeta map[string]string
Datacenter string
Service *AgentService
Check *AgentCheck
diff --git a/api/catalog_test.go b/api/catalog_test.go
index e37d3dd50..527153b32 100644
--- a/api/catalog_test.go
+++ b/api/catalog_test.go
@@ -60,6 +60,64 @@ func TestCatalog_Nodes(t *testing.T) {
})
}
+func TestCatalog_Nodes_MetaFilter(t *testing.T) {
+ meta := map[string]string{"somekey": "somevalue"}
+ c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
+ conf.NodeMeta = meta
+ })
+ defer s.Stop()
+
+ catalog := c.Catalog()
+
+ // Make sure we get the node back when filtering by its metadata
+ testutil.WaitForResult(func() (bool, error) {
+ nodes, meta, err := catalog.Nodes(&QueryOptions{NodeMeta: meta})
+ if err != nil {
+ return false, err
+ }
+
+ if meta.LastIndex == 0 {
+ return false, fmt.Errorf("Bad: %v", meta)
+ }
+
+ if len(nodes) == 0 {
+ return false, fmt.Errorf("Bad: %v", nodes)
+ }
+
+ if _, ok := nodes[0].TaggedAddresses["wan"]; !ok {
+ return false, fmt.Errorf("Bad: %v", nodes[0])
+ }
+
+ if v, ok := nodes[0].Meta["somekey"]; !ok || v != "somevalue" {
+ return false, fmt.Errorf("Bad: %v", nodes[0].Meta)
+ }
+
+ return true, nil
+ }, func(err error) {
+ t.Fatalf("err: %s", err)
+ })
+
+ // Get nothing back when we use an invalid filter
+ testutil.WaitForResult(func() (bool, error) {
+ nodes, meta, err := catalog.Nodes(&QueryOptions{NodeMeta: map[string]string{"nope":"nope"}})
+ if err != nil {
+ return false, err
+ }
+
+ if meta.LastIndex == 0 {
+ return false, fmt.Errorf("Bad: %v", meta)
+ }
+
+ if len(nodes) != 0 {
+ return false, fmt.Errorf("Bad: %v", nodes)
+ }
+
+ return true, nil
+ }, func(err error) {
+ t.Fatalf("err: %s", err)
+ })
+}
+
func TestCatalog_Services(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
@@ -87,6 +145,56 @@ func TestCatalog_Services(t *testing.T) {
})
}
+func TestCatalog_Services_NodeMetaFilter(t *testing.T) {
+ meta := map[string]string{"somekey": "somevalue"}
+ c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) {
+ conf.NodeMeta = meta
+ })
+ defer s.Stop()
+
+ catalog := c.Catalog()
+
+ // Make sure we get the service back when filtering by the node's metadata
+ testutil.WaitForResult(func() (bool, error) {
+ services, meta, err := catalog.Services(&QueryOptions{NodeMeta: meta})
+ if err != nil {
+ return false, err
+ }
+
+ if meta.LastIndex == 0 {
+ return false, fmt.Errorf("Bad: %v", meta)
+ }
+
+ if len(services) == 0 {
+ return false, fmt.Errorf("Bad: %v", services)
+ }
+
+ return true, nil
+ }, func(err error) {
+ t.Fatalf("err: %s", err)
+ })
+
+ // Get nothing back when using an invalid filter
+ testutil.WaitForResult(func() (bool, error) {
+ services, meta, err := catalog.Services(&QueryOptions{NodeMeta: map[string]string{"nope":"nope"}})
+ if err != nil {
+ return false, err
+ }
+
+ if meta.LastIndex == 0 {
+ return false, fmt.Errorf("Bad: %v", meta)
+ }
+
+ if len(services) != 0 {
+ return false, fmt.Errorf("Bad: %v", services)
+ }
+
+ return true, nil
+ }, func(err error) {
+ t.Fatalf("err: %s", err)
+ })
+}
+
func TestCatalog_Service(t *testing.T) {
t.Parallel()
c, s := makeClient(t)
@@ -173,6 +281,7 @@ func TestCatalog_Registration(t *testing.T) {
Datacenter: "dc1",
Node: "foobar",
Address: "192.168.10.10",
+ NodeMeta: map[string]string{"somekey": "somevalue"},
Service: service,
Check: check,
}
@@ -200,6 +309,10 @@ func TestCatalog_Registration(t *testing.T) {
return false, fmt.Errorf("missing checkid service:redis1")
}
+ if v, ok := node.Node.Meta["somekey"]; !ok || v != "somevalue" {
+ return false, fmt.Errorf("missing node meta pair somekey:somevalue")
+ }
+
return true, nil
}, func(err error) {
t.Fatalf("err: %s", err)
diff --git a/testutil/server.go b/testutil/server.go
index 8ab196eca..e90121781 100644
--- a/testutil/server.go
+++ b/testutil/server.go
@@ -53,6 +53,7 @@ type TestAddressConfig struct {
// TestServerConfig is the main server configuration struct.
type TestServerConfig struct {
NodeName string `json:"node_name"`
+ NodeMeta map[string]string `json:"node_meta"`
Performance *TestPerformanceConfig `json:"performance,omitempty"`
Bootstrap bool `json:"bootstrap,omitempty"`
Server bool `json:"server,omitempty"`
diff --git a/website/source/docs/agent/http/agent.html.markdown b/website/source/docs/agent/http/agent.html.markdown
index c1f6e4775..b01a30033 100644
--- a/website/source/docs/agent/http/agent.html.markdown
+++ b/website/source/docs/agent/http/agent.html.markdown
@@ -128,6 +128,8 @@ This endpoint is used to return the configuration and member information of the
Consul 0.7.0 and later also includes a snapshot of various operating statistics under the `Stats` key. These statistics are intended to help human operators for debugging and may change over time, so this part of the interface should not be consumed programmatically.
+Consul 0.7.3 and later also includes a block of user-defined node metadata values under the `Meta` key. These are arbitrary key/value pairs defined in the [node meta](/docs/agent/options.html#_node_meta) section of the agent configuration.
+
It returns a JSON body like this:
```javascript
@@ -194,6 +196,10 @@ It returns a JSON body like this:
"DelegateMin": 2,
"DelegateMax": 4,
"DelegateCur": 4
+ },
+ "Meta": {
+ "instance_type": "i2.xlarge",
+ "os_version": "ubuntu_16.04",
}
}
```
diff --git a/website/source/docs/agent/http/catalog.html.markdown b/website/source/docs/agent/http/catalog.html.markdown
index e5dcaab16..0a7a06629 100644
--- a/website/source/docs/agent/http/catalog.html.markdown
+++ b/website/source/docs/agent/http/catalog.html.markdown
@@ -44,6 +44,9 @@ body must look something like:
"lan": "192.168.10.10",
"wan": "10.0.10.10"
},
+ "NodeMeta": {
+ "somekey": "somevalue"
+ },
"Service": {
"ID": "redis1",
"Service": "redis",
@@ -73,6 +76,10 @@ the node with the catalog. `TaggedAddresses` can be used in conjunction with the
option and the `wan` address. The `lan` address was added in Consul 0.7 to help find
the LAN address if address translation is enabled.
+The `Meta` block was added in Consul 0.7.3 to enable associating arbitrary metadata
+key/value pairs with a node for filtering purposes. For more information on node metadata,
+see the [node meta](/docs/agent/options.html#_node_meta) section of the configuration page.
+
If the `Service` key is provided, the service will also be registered. If
`ID` is not provided, it will be defaulted to the value of the `Service.Service` property.
Only one service with a given `ID` may be present per node. The service `Tags`, `Address`,
@@ -191,9 +198,9 @@ the node list in ascending order based on the estimated round trip
time from that node. Passing `?near=_agent` will use the agent's
node for the sort.
-Adding the optional `?node-meta=` parameter with a desired node
-metadata key/value pair of the form `key:value` will filter the
-results to nodes with that pair present.
+In Consul 0.7.3 and later, the optional `?node-meta=` parameter can be
+provided with a desired node metadata key/value pair of the form `key:value`.
+This will filter the results to nodes with that pair present.
It returns a JSON body like this:
@@ -205,6 +212,9 @@ It returns a JSON body like this:
"TaggedAddresses": {
"lan": "10.1.10.11",
"wan": "10.1.10.11"
+ },
+ "Meta": {
+ "instance_type": "t2.medium"
}
},
{
@@ -213,6 +223,9 @@ It returns a JSON body like this:
"TaggedAddresses": {
"lan": "10.1.10.11",
"wan": "10.1.10.12"
+ },
+ "Meta": {
+ "instance_type": "t2.large"
}
}
]
@@ -226,9 +239,9 @@ This endpoint is hit with a `GET` and returns the services registered
in a given DC. By default, the datacenter of the agent is queried;
however, the `dc` can be provided using the `?dc=` query parameter.
-Adding the optional `?node-meta=` parameter with a desired node
-metadata key/value pair of the form `key:value` will filter the
-results to services with that pair present.
+In Consul 0.7.3 and later, the optional `?node-meta=` parameter can be
+provided with a desired node metadata key/value pair of the form `key:value`.
+This will filter the results to services with that pair present.
It returns a JSON body like this:
@@ -273,6 +286,9 @@ It returns a JSON body like this:
"lan": "192.168.10.10",
"wan": "10.0.10.10"
},
+ "Meta": {
+ "instance_type": "t2.medium"
+ }
"CreateIndex": 51,
"ModifyIndex": 51,
"Node": "foobar",
@@ -294,6 +310,7 @@ The returned fields are as follows:
- `Address`: IP address of the Consul node on which the service is registered
- `TaggedAddresses`: List of explicit LAN and WAN IP addresses for the agent
+- `Meta`: Added in Consul 0.7.3, a list of user-defined metadata key/value pairs for the node
- `CreateIndex`: Internal index value representing when the service was created
- `ModifyIndex`: Last index that modified the service
- `Node`: Node name of the Consul node on which the service is registered
@@ -321,6 +338,9 @@ It returns a JSON body like this:
"TaggedAddresses": {
"lan": "10.1.10.12",
"wan": "10.1.10.12"
+ },
+ "Meta": {
+ "instance_type": "t2.medium"
}
},
"Services": {
diff --git a/website/source/docs/agent/http/health.html.markdown b/website/source/docs/agent/http/health.html.markdown
index 9c8767ef7..e9f18c641 100644
--- a/website/source/docs/agent/http/health.html.markdown
+++ b/website/source/docs/agent/http/health.html.markdown
@@ -131,6 +131,9 @@ It returns a JSON body like this:
"TaggedAddresses": {
"lan": "10.1.10.12",
"wan": "10.1.10.12"
+ },
+ "Meta": {
+ "instance_type": "t2.medium"
}
},
"Service": {
diff --git a/website/source/docs/agent/options.html.markdown b/website/source/docs/agent/options.html.markdown
index 3a7d6f8c0..0f9196359 100644
--- a/website/source/docs/agent/options.html.markdown
+++ b/website/source/docs/agent/options.html.markdown
@@ -251,9 +251,9 @@ will exit with an error at startup.
* `-node` - The name of this node in the cluster.
This must be unique within the cluster. By default this is the hostname of the machine.
-* `-node-meta` - An arbitrary metadata key/value pair
- to associate with the node, of the form `key:value`. This can be specified multiple times. Node metadata
- pairs have the following restrictions:
+* `-node-meta` - Available in Consul 0.7.3 and later,
+ this specifies an arbitrary metadata key/value pair to associate with the node, of the form `key:value`.
+ This can be specified multiple times. Node metadata pairs have the following restrictions:
- A maximum of 64 key/value pairs can be registered per node.
- Metadata keys must be between 1 and 128 characters (inclusive) in length
- Metadata keys must contain only alphanumeric, `-`, and `_` characters.
@@ -667,9 +667,18 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass
* `node_name` Equivalent to the
[`-node` command-line flag](#_node).
-* `node_meta` This object allows associating arbitrary
- metadata key/value pairs with the local node, which can then be used for filtering results from certain
- catalog endpoints. See the [`-node-meta` command-line flag](#_node_meta) for more information.
+* `node_meta` Available in Consul 0.7.3 and later,
+ This object allows associating arbitrary metadata key/value pairs with the local node, which can
+ then be used for filtering results from certain catalog endpoints. See the
+ [`-node-meta` command-line flag](#_node_meta) for more information.
+
+ ```javascript
+ {
+ "node_meta": {
+ "instance_type": "t2.medium"
+ }
+ }
+ ```
* `performance` Available in Consul 0.7 and
later, this is a nested object that allows tuning the performance of different subsystems in