Update client api and docs for node metadata

This commit is contained in:
Kyle Havlovitz 2017-01-11 18:44:13 -05:00
parent 561d6c71e0
commit 15f008b3e3
No known key found for this signature in database
GPG Key ID: 8A5E6B173056AD6C
8 changed files with 177 additions and 12 deletions

View File

@ -74,6 +74,11 @@ type QueryOptions struct {
// that node. Setting this to "_agent" will use the agent's node // that node. Setting this to "_agent" will use the agent's node
// for the sort. // for the sort.
Near string 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 // WriteOptions are used to parameterize a write
@ -386,6 +391,11 @@ func (r *request) setQueryOptions(q *QueryOptions) {
if q.Near != "" { if q.Near != "" {
r.params.Set("near", 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 // durToMsec converts a duration to a millisecond specified string. If the

View File

@ -4,12 +4,14 @@ type Node struct {
Node string Node string
Address string Address string
TaggedAddresses map[string]string TaggedAddresses map[string]string
Meta map[string]string
} }
type CatalogService struct { type CatalogService struct {
Node string Node string
Address string Address string
TaggedAddresses map[string]string TaggedAddresses map[string]string
NodeMeta map[string]string
ServiceID string ServiceID string
ServiceName string ServiceName string
ServiceAddress string ServiceAddress string
@ -29,6 +31,7 @@ type CatalogRegistration struct {
Node string Node string
Address string Address string
TaggedAddresses map[string]string TaggedAddresses map[string]string
NodeMeta map[string]string
Datacenter string Datacenter string
Service *AgentService Service *AgentService
Check *AgentCheck Check *AgentCheck

View File

@ -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) { func TestCatalog_Services(t *testing.T) {
t.Parallel() t.Parallel()
c, s := makeClient(t) 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) { func TestCatalog_Service(t *testing.T) {
t.Parallel() t.Parallel()
c, s := makeClient(t) c, s := makeClient(t)
@ -173,6 +281,7 @@ func TestCatalog_Registration(t *testing.T) {
Datacenter: "dc1", Datacenter: "dc1",
Node: "foobar", Node: "foobar",
Address: "192.168.10.10", Address: "192.168.10.10",
NodeMeta: map[string]string{"somekey": "somevalue"},
Service: service, Service: service,
Check: check, Check: check,
} }
@ -200,6 +309,10 @@ func TestCatalog_Registration(t *testing.T) {
return false, fmt.Errorf("missing checkid service:redis1") 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 return true, nil
}, func(err error) { }, func(err error) {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)

View File

@ -53,6 +53,7 @@ type TestAddressConfig struct {
// TestServerConfig is the main server configuration struct. // TestServerConfig is the main server configuration struct.
type TestServerConfig struct { type TestServerConfig struct {
NodeName string `json:"node_name"` NodeName string `json:"node_name"`
NodeMeta map[string]string `json:"node_meta"`
Performance *TestPerformanceConfig `json:"performance,omitempty"` Performance *TestPerformanceConfig `json:"performance,omitempty"`
Bootstrap bool `json:"bootstrap,omitempty"` Bootstrap bool `json:"bootstrap,omitempty"`
Server bool `json:"server,omitempty"` Server bool `json:"server,omitempty"`

View File

@ -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.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: It returns a JSON body like this:
```javascript ```javascript
@ -194,6 +196,10 @@ It returns a JSON body like this:
"DelegateMin": 2, "DelegateMin": 2,
"DelegateMax": 4, "DelegateMax": 4,
"DelegateCur": 4 "DelegateCur": 4
},
"Meta": {
"instance_type": "i2.xlarge",
"os_version": "ubuntu_16.04",
} }
} }
``` ```

View File

@ -44,6 +44,9 @@ body must look something like:
"lan": "192.168.10.10", "lan": "192.168.10.10",
"wan": "10.0.10.10" "wan": "10.0.10.10"
}, },
"NodeMeta": {
"somekey": "somevalue"
},
"Service": { "Service": {
"ID": "redis1", "ID": "redis1",
"Service": "redis", "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 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 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 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. `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`, 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 time from that node. Passing `?near=_agent` will use the agent's
node for the sort. node for the sort.
Adding the optional `?node-meta=` parameter with a desired node In Consul 0.7.3 and later, the optional `?node-meta=` parameter can be
metadata key/value pair of the form `key:value` will filter the provided with a desired node metadata key/value pair of the form `key:value`.
results to nodes with that pair present. This will filter the results to nodes with that pair present.
It returns a JSON body like this: It returns a JSON body like this:
@ -205,6 +212,9 @@ It returns a JSON body like this:
"TaggedAddresses": { "TaggedAddresses": {
"lan": "10.1.10.11", "lan": "10.1.10.11",
"wan": "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": { "TaggedAddresses": {
"lan": "10.1.10.11", "lan": "10.1.10.11",
"wan": "10.1.10.12" "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; in a given DC. By default, the datacenter of the agent is queried;
however, the `dc` can be provided using the `?dc=` query parameter. however, the `dc` can be provided using the `?dc=` query parameter.
Adding the optional `?node-meta=` parameter with a desired node In Consul 0.7.3 and later, the optional `?node-meta=` parameter can be
metadata key/value pair of the form `key:value` will filter the provided with a desired node metadata key/value pair of the form `key:value`.
results to services with that pair present. This will filter the results to services with that pair present.
It returns a JSON body like this: It returns a JSON body like this:
@ -273,6 +286,9 @@ It returns a JSON body like this:
"lan": "192.168.10.10", "lan": "192.168.10.10",
"wan": "10.0.10.10" "wan": "10.0.10.10"
}, },
"Meta": {
"instance_type": "t2.medium"
}
"CreateIndex": 51, "CreateIndex": 51,
"ModifyIndex": 51, "ModifyIndex": 51,
"Node": "foobar", "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 - `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 - `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 - `CreateIndex`: Internal index value representing when the service was created
- `ModifyIndex`: Last index that modified the service - `ModifyIndex`: Last index that modified the service
- `Node`: Node name of the Consul node on which the service is registered - `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": { "TaggedAddresses": {
"lan": "10.1.10.12", "lan": "10.1.10.12",
"wan": "10.1.10.12" "wan": "10.1.10.12"
},
"Meta": {
"instance_type": "t2.medium"
} }
}, },
"Services": { "Services": {

View File

@ -131,6 +131,9 @@ It returns a JSON body like this:
"TaggedAddresses": { "TaggedAddresses": {
"lan": "10.1.10.12", "lan": "10.1.10.12",
"wan": "10.1.10.12" "wan": "10.1.10.12"
},
"Meta": {
"instance_type": "t2.medium"
} }
}, },
"Service": { "Service": {

View File

@ -251,9 +251,9 @@ will exit with an error at startup.
* <a name="_node"></a><a href="#_node">`-node`</a> - The name of this node in the cluster. * <a name="_node"></a><a href="#_node">`-node`</a> - The name of this node in the cluster.
This must be unique within the cluster. By default this is the hostname of the machine. This must be unique within the cluster. By default this is the hostname of the machine.
* <a name="_node_meta"></a><a href="#_node_meta">`-node-meta`</a> - An arbitrary metadata key/value pair * <a name="_node_meta"></a><a href="#_node_meta">`-node-meta`</a> - Available in Consul 0.7.3 and later,
to associate with the node, of the form `key:value`. This can be specified multiple times. Node metadata this specifies an arbitrary metadata key/value pair to associate with the node, of the form `key:value`.
pairs have the following restrictions: 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. - 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 be between 1 and 128 characters (inclusive) in length
- Metadata keys must contain only alphanumeric, `-`, and `_` characters. - 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
* <a name="node_name"></a><a href="#node_name">`node_name`</a> Equivalent to the * <a name="node_name"></a><a href="#node_name">`node_name`</a> Equivalent to the
[`-node` command-line flag](#_node). [`-node` command-line flag](#_node).
* <a name="node_meta"></a><a href="#node_meta">`node_meta`</a> This object allows associating arbitrary * <a name="node_meta"></a><a href="#node_meta">`node_meta`</a> Available in Consul 0.7.3 and later,
metadata key/value pairs with the local node, which can then be used for filtering results from certain This object allows associating arbitrary metadata key/value pairs with the local node, which can
catalog endpoints. See the [`-node-meta` command-line flag](#_node_meta) for more information. 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"
}
}
```
* <a name="performance"></a><a href="#performance">`performance`</a> Available in Consul 0.7 and * <a name="performance"></a><a href="#performance">`performance`</a> Available in Consul 0.7 and
later, this is a nested object that allows tuning the performance of different subsystems in later, this is a nested object that allows tuning the performance of different subsystems in