mirror of https://github.com/status-im/consul.git
Add support for multiple metadata filters to remaining endpoints
Enabled multiple meta filters for /v1/catalog/nodes and /v1/catalog/services
This commit is contained in:
parent
5acd69b4fc
commit
9e696220a8
|
@ -198,11 +198,8 @@ func (s *StateStore) Nodes() (uint64, structs.Nodes, error) {
|
|||
return idx, results, nil
|
||||
}
|
||||
|
||||
// NodesByMeta is used to return all nodes with the given meta key/value pair.
|
||||
// NodesByMeta is used to return all nodes with the given metadata key/value pairs.
|
||||
func (s *StateStore) NodesByMeta(filters map[string]string) (uint64, structs.Nodes, error) {
|
||||
if len(filters) > 1 {
|
||||
return 0, nil, fmt.Errorf("multiple meta filters not supported")
|
||||
}
|
||||
tx := s.db.Txn(false)
|
||||
defer tx.Abort()
|
||||
|
||||
|
@ -213,6 +210,7 @@ func (s *StateStore) NodesByMeta(filters map[string]string) (uint64, structs.Nod
|
|||
var args []interface{}
|
||||
for key, value := range filters {
|
||||
args = append(args, key, value)
|
||||
break
|
||||
}
|
||||
nodes, err := tx.Get("nodes", "meta", args...)
|
||||
if err != nil {
|
||||
|
@ -222,7 +220,10 @@ func (s *StateStore) NodesByMeta(filters map[string]string) (uint64, structs.Nod
|
|||
// Create and return the nodes list.
|
||||
var results structs.Nodes
|
||||
for node := nodes.Next(); node != nil; node = nodes.Next() {
|
||||
results = append(results, node.(*structs.Node))
|
||||
n := node.(*structs.Node)
|
||||
if len(filters) <= 1 || structs.SatisfiesMetaFilters(n.Meta, filters) {
|
||||
results = append(results, n)
|
||||
}
|
||||
}
|
||||
return idx, results, nil
|
||||
}
|
||||
|
@ -437,11 +438,8 @@ func (s *StateStore) Services() (uint64, structs.Services, error) {
|
|||
return idx, results, nil
|
||||
}
|
||||
|
||||
// Services returns all services, filtered by the given node metadata.
|
||||
// ServicesByNodeMeta returns all services, filtered by the given node metadata.
|
||||
func (s *StateStore) ServicesByNodeMeta(filters map[string]string) (uint64, structs.Services, error) {
|
||||
if len(filters) > 1 {
|
||||
return 0, nil, fmt.Errorf("multiple meta filters not supported")
|
||||
}
|
||||
tx := s.db.Txn(false)
|
||||
defer tx.Abort()
|
||||
|
||||
|
@ -452,6 +450,7 @@ func (s *StateStore) ServicesByNodeMeta(filters map[string]string) (uint64, stru
|
|||
var args []interface{}
|
||||
for key, value := range filters {
|
||||
args = append(args, key, value)
|
||||
break
|
||||
}
|
||||
nodes, err := tx.Get("nodes", "meta", args...)
|
||||
if err != nil {
|
||||
|
@ -462,6 +461,9 @@ func (s *StateStore) ServicesByNodeMeta(filters map[string]string) (uint64, stru
|
|||
unique := make(map[string]map[string]struct{})
|
||||
for node := nodes.Next(); node != nil; node = nodes.Next() {
|
||||
n := node.(*structs.Node)
|
||||
if len(filters) > 1 && !structs.SatisfiesMetaFilters(n.Meta, filters) {
|
||||
continue
|
||||
}
|
||||
// List all the services on the node
|
||||
services, err := tx.Get("services", "node", n.Node)
|
||||
if err != nil {
|
||||
|
@ -878,8 +880,8 @@ func (s *StateStore) ServiceChecks(serviceName string) (uint64, structs.HealthCh
|
|||
}
|
||||
|
||||
// ServiceChecksByNodeMeta is used to get all checks associated with a
|
||||
// given service ID. The query is performed against a service
|
||||
// _name_ instead of a service ID.
|
||||
// given service ID, filtered by the given node metadata values. The query
|
||||
// is performed against a service _name_ instead of a service ID.
|
||||
func (s *StateStore) ServiceChecksByNodeMeta(serviceName string, filters map[string]string) (uint64, structs.HealthChecks, error) {
|
||||
tx := s.db.Txn(false)
|
||||
defer tx.Abort()
|
||||
|
@ -921,8 +923,8 @@ func (s *StateStore) ChecksInState(state string) (uint64, structs.HealthChecks,
|
|||
return s.parseChecks(idx, checks)
|
||||
}
|
||||
|
||||
// ChecksInState is used to query the state store for all checks
|
||||
// which are in the provided state.
|
||||
// ChecksInStateByNodeMeta is used to query the state store for all checks
|
||||
// which are in the provided state, filtered by the given node metadata values.
|
||||
func (s *StateStore) ChecksInStateByNodeMeta(state string, filters map[string]string) (uint64, structs.HealthChecks, error) {
|
||||
tx := s.db.Txn(false)
|
||||
defer tx.Abort()
|
||||
|
|
|
@ -545,65 +545,50 @@ func TestStateStore_GetNodesByMeta(t *testing.T) {
|
|||
}
|
||||
|
||||
// Create some nodes in the state store
|
||||
node0 := &structs.Node{Node: "node0", Address: "127.0.0.1", Meta: map[string]string{"role": "client", "common": "1"}}
|
||||
if err := s.EnsureNode(0, node0); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
node1 := &structs.Node{Node: "node1", Address: "127.0.0.1", Meta: map[string]string{"role": "server", "common": "1"}}
|
||||
if err := s.EnsureNode(1, node1); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
testRegisterNodeWithMeta(t, s, 0, "node0", map[string]string{"role": "client"})
|
||||
testRegisterNodeWithMeta(t, s, 1, "node1", map[string]string{"role": "client", "common": "1"})
|
||||
testRegisterNodeWithMeta(t, s, 2, "node2", map[string]string{"role": "server", "common": "1"})
|
||||
|
||||
cases := []struct {
|
||||
filters map[string]string
|
||||
nodes []string
|
||||
}{
|
||||
// Simple meta filter
|
||||
{
|
||||
filters: map[string]string{"role": "server"},
|
||||
nodes: []string{"node2"},
|
||||
},
|
||||
// Common meta filter
|
||||
{
|
||||
filters: map[string]string{"common": "1"},
|
||||
nodes: []string{"node1", "node2"},
|
||||
},
|
||||
// Invalid meta filter
|
||||
{
|
||||
filters: map[string]string{"invalid": "nope"},
|
||||
nodes: []string{},
|
||||
},
|
||||
// Multiple meta filters
|
||||
{
|
||||
filters: map[string]string{"role": "client", "common": "1"},
|
||||
nodes: []string{"node1"},
|
||||
},
|
||||
}
|
||||
|
||||
// Retrieve the node with role=client
|
||||
idx, nodes, err := s.NodesByMeta(map[string]string{"role": "client"})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if idx != 1 {
|
||||
t.Fatalf("bad index: %d", idx)
|
||||
}
|
||||
|
||||
// Only one node was returned
|
||||
if n := len(nodes); n != 1 {
|
||||
t.Fatalf("bad node count: %d", n)
|
||||
}
|
||||
|
||||
// Make sure the node is correct
|
||||
if nodes[0].CreateIndex != 0 || nodes[0].ModifyIndex != 0 {
|
||||
t.Fatalf("bad node index: %d, %d", nodes[0].CreateIndex, nodes[0].ModifyIndex)
|
||||
}
|
||||
if nodes[0].Node != "node0" {
|
||||
t.Fatalf("bad: %#v", nodes[0])
|
||||
}
|
||||
if !reflect.DeepEqual(nodes[0].Meta, node0.Meta) {
|
||||
t.Fatalf("bad: %v != %v", nodes[0].Meta, node0.Meta)
|
||||
}
|
||||
|
||||
// Retrieve both nodes via their common meta field
|
||||
idx, nodes, err = s.NodesByMeta(map[string]string{"common": "1"})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if idx != 1 {
|
||||
t.Fatalf("bad index: %d", idx)
|
||||
}
|
||||
|
||||
// All nodes were returned
|
||||
if n := len(nodes); n != 2 {
|
||||
t.Fatalf("bad node count: %d", n)
|
||||
}
|
||||
|
||||
// Make sure the nodes match
|
||||
for i, node := range nodes {
|
||||
if node.CreateIndex != uint64(i) || node.ModifyIndex != uint64(i) {
|
||||
t.Fatalf("bad node index: %d, %d", node.CreateIndex, node.ModifyIndex)
|
||||
for _, tc := range cases {
|
||||
_, result, err := s.NodesByMeta(tc.filters)
|
||||
if err != nil {
|
||||
t.Fatalf("bad: %v", err)
|
||||
}
|
||||
name := fmt.Sprintf("node%d", i)
|
||||
if node.Node != name {
|
||||
t.Fatalf("bad: %#v", node)
|
||||
|
||||
if len(result) != len(tc.nodes) {
|
||||
t.Fatalf("bad: %v %v", result, tc.nodes)
|
||||
}
|
||||
if v, ok := node.Meta["common"]; !ok || v != "1" {
|
||||
t.Fatalf("bad: %v", node.Meta)
|
||||
|
||||
for i, node := range result {
|
||||
if node.Node != tc.nodes[i] {
|
||||
t.Fatalf("bad: %v %v", node.Node, tc.nodes[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -976,13 +961,10 @@ func TestStateStore_ServicesByNodeMeta(t *testing.T) {
|
|||
}
|
||||
|
||||
// Filter the services by the first node's meta value
|
||||
idx, res, err = s.ServicesByNodeMeta(map[string]string{"role": "client"})
|
||||
_, res, err = s.ServicesByNodeMeta(map[string]string{"role": "client"})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if idx != 3 {
|
||||
t.Fatalf("bad index: %d", idx)
|
||||
}
|
||||
expected := structs.Services{
|
||||
"redis": []string{"master", "prod"},
|
||||
}
|
||||
|
@ -992,13 +974,10 @@ func TestStateStore_ServicesByNodeMeta(t *testing.T) {
|
|||
}
|
||||
|
||||
// Get all services using the common meta value
|
||||
idx, res, err = s.ServicesByNodeMeta(map[string]string{"common": "1"})
|
||||
_, res, err = s.ServicesByNodeMeta(map[string]string{"common": "1"})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if idx != 3 {
|
||||
t.Fatalf("bad index: %d", idx)
|
||||
}
|
||||
expected = structs.Services{
|
||||
"redis": []string{"master", "prod", "slave"},
|
||||
}
|
||||
|
@ -1006,6 +985,29 @@ func TestStateStore_ServicesByNodeMeta(t *testing.T) {
|
|||
if !reflect.DeepEqual(res, expected) {
|
||||
t.Fatalf("bad: %v %v", res, expected)
|
||||
}
|
||||
|
||||
// Get an empty list for an invalid meta value
|
||||
_, res, err = s.ServicesByNodeMeta(map[string]string{"invalid": "nope"})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
expected = structs.Services{}
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
t.Fatalf("bad: %v %v", res, expected)
|
||||
}
|
||||
|
||||
// Get the first node's service instance using multiple meta filters
|
||||
_, res, err = s.ServicesByNodeMeta(map[string]string{"role": "client", "common": "1"})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
expected = structs.Services{
|
||||
"redis": []string{"master", "prod"},
|
||||
}
|
||||
sort.Strings(res["redis"])
|
||||
if !reflect.DeepEqual(res, expected) {
|
||||
t.Fatalf("bad: %v %v", res, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStateStore_ServiceNodes(t *testing.T) {
|
||||
|
|
|
@ -200,7 +200,8 @@ node for the sort.
|
|||
|
||||
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.
|
||||
This parameter can be specified multiple times, and will filter the results to
|
||||
nodes with the specified key/value pair(s).
|
||||
|
||||
It returns a JSON body like this:
|
||||
|
||||
|
@ -241,7 +242,8 @@ however, the `dc` can be provided using the `?dc=` query parameter.
|
|||
|
||||
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.
|
||||
This parameter can be specified multiple times, and will filter the results to
|
||||
services on nodes with the specified key/value pair(s).
|
||||
|
||||
It returns a JSON body like this:
|
||||
|
||||
|
|
Loading…
Reference in New Issue