mirror of
https://github.com/status-im/consul.git
synced 2025-02-18 08:36:46 +00:00
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
|
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) {
|
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)
|
tx := s.db.Txn(false)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
|
|
||||||
@ -213,6 +210,7 @@ func (s *StateStore) NodesByMeta(filters map[string]string) (uint64, structs.Nod
|
|||||||
var args []interface{}
|
var args []interface{}
|
||||||
for key, value := range filters {
|
for key, value := range filters {
|
||||||
args = append(args, key, value)
|
args = append(args, key, value)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
nodes, err := tx.Get("nodes", "meta", args...)
|
nodes, err := tx.Get("nodes", "meta", args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -222,7 +220,10 @@ func (s *StateStore) NodesByMeta(filters map[string]string) (uint64, structs.Nod
|
|||||||
// Create and return the nodes list.
|
// Create and return the nodes list.
|
||||||
var results structs.Nodes
|
var results structs.Nodes
|
||||||
for node := nodes.Next(); node != nil; node = nodes.Next() {
|
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
|
return idx, results, nil
|
||||||
}
|
}
|
||||||
@ -437,11 +438,8 @@ func (s *StateStore) Services() (uint64, structs.Services, error) {
|
|||||||
return idx, results, nil
|
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) {
|
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)
|
tx := s.db.Txn(false)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
|
|
||||||
@ -452,6 +450,7 @@ func (s *StateStore) ServicesByNodeMeta(filters map[string]string) (uint64, stru
|
|||||||
var args []interface{}
|
var args []interface{}
|
||||||
for key, value := range filters {
|
for key, value := range filters {
|
||||||
args = append(args, key, value)
|
args = append(args, key, value)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
nodes, err := tx.Get("nodes", "meta", args...)
|
nodes, err := tx.Get("nodes", "meta", args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -462,6 +461,9 @@ func (s *StateStore) ServicesByNodeMeta(filters map[string]string) (uint64, stru
|
|||||||
unique := make(map[string]map[string]struct{})
|
unique := make(map[string]map[string]struct{})
|
||||||
for node := nodes.Next(); node != nil; node = nodes.Next() {
|
for node := nodes.Next(); node != nil; node = nodes.Next() {
|
||||||
n := node.(*structs.Node)
|
n := node.(*structs.Node)
|
||||||
|
if len(filters) > 1 && !structs.SatisfiesMetaFilters(n.Meta, filters) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// List all the services on the node
|
// List all the services on the node
|
||||||
services, err := tx.Get("services", "node", n.Node)
|
services, err := tx.Get("services", "node", n.Node)
|
||||||
if err != nil {
|
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
|
// ServiceChecksByNodeMeta is used to get all checks associated with a
|
||||||
// given service ID. The query is performed against a service
|
// given service ID, filtered by the given node metadata values. The query
|
||||||
// _name_ instead of a service ID.
|
// 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) {
|
func (s *StateStore) ServiceChecksByNodeMeta(serviceName string, filters map[string]string) (uint64, structs.HealthChecks, error) {
|
||||||
tx := s.db.Txn(false)
|
tx := s.db.Txn(false)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
@ -921,8 +923,8 @@ func (s *StateStore) ChecksInState(state string) (uint64, structs.HealthChecks,
|
|||||||
return s.parseChecks(idx, checks)
|
return s.parseChecks(idx, checks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChecksInState is used to query the state store for all checks
|
// ChecksInStateByNodeMeta is used to query the state store for all checks
|
||||||
// which are in the provided state.
|
// 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) {
|
func (s *StateStore) ChecksInStateByNodeMeta(state string, filters map[string]string) (uint64, structs.HealthChecks, error) {
|
||||||
tx := s.db.Txn(false)
|
tx := s.db.Txn(false)
|
||||||
defer tx.Abort()
|
defer tx.Abort()
|
||||||
|
@ -545,65 +545,50 @@ func TestStateStore_GetNodesByMeta(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create some nodes in the state store
|
// 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"}}
|
testRegisterNodeWithMeta(t, s, 0, "node0", map[string]string{"role": "client"})
|
||||||
if err := s.EnsureNode(0, node0); err != nil {
|
testRegisterNodeWithMeta(t, s, 1, "node1", map[string]string{"role": "client", "common": "1"})
|
||||||
t.Fatalf("err: %v", err)
|
testRegisterNodeWithMeta(t, s, 2, "node2", map[string]string{"role": "server", "common": "1"})
|
||||||
}
|
|
||||||
node1 := &structs.Node{Node: "node1", Address: "127.0.0.1", Meta: map[string]string{"role": "server", "common": "1"}}
|
cases := []struct {
|
||||||
if err := s.EnsureNode(1, node1); err != nil {
|
filters map[string]string
|
||||||
t.Fatalf("err: %v", err)
|
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
|
for _, tc := range cases {
|
||||||
idx, nodes, err := s.NodesByMeta(map[string]string{"role": "client"})
|
_, result, err := s.NodesByMeta(tc.filters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("bad: %v", err)
|
||||||
}
|
|
||||||
if idx != 1 {
|
|
||||||
t.Fatalf("bad index: %d", idx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only one node was returned
|
if len(result) != len(tc.nodes) {
|
||||||
if n := len(nodes); n != 1 {
|
t.Fatalf("bad: %v %v", result, tc.nodes)
|
||||||
t.Fatalf("bad node count: %d", n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the node is correct
|
for i, node := range result {
|
||||||
if nodes[0].CreateIndex != 0 || nodes[0].ModifyIndex != 0 {
|
if node.Node != tc.nodes[i] {
|
||||||
t.Fatalf("bad node index: %d, %d", nodes[0].CreateIndex, nodes[0].ModifyIndex)
|
t.Fatalf("bad: %v %v", node.Node, tc.nodes[i])
|
||||||
}
|
}
|
||||||
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)
|
|
||||||
}
|
|
||||||
name := fmt.Sprintf("node%d", i)
|
|
||||||
if node.Node != name {
|
|
||||||
t.Fatalf("bad: %#v", node)
|
|
||||||
}
|
|
||||||
if v, ok := node.Meta["common"]; !ok || v != "1" {
|
|
||||||
t.Fatalf("bad: %v", node.Meta)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -976,13 +961,10 @@ func TestStateStore_ServicesByNodeMeta(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter the services by the first node's meta value
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
if idx != 3 {
|
|
||||||
t.Fatalf("bad index: %d", idx)
|
|
||||||
}
|
|
||||||
expected := structs.Services{
|
expected := structs.Services{
|
||||||
"redis": []string{"master", "prod"},
|
"redis": []string{"master", "prod"},
|
||||||
}
|
}
|
||||||
@ -992,13 +974,10 @@ func TestStateStore_ServicesByNodeMeta(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get all services using the common meta value
|
// 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 {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
if idx != 3 {
|
|
||||||
t.Fatalf("bad index: %d", idx)
|
|
||||||
}
|
|
||||||
expected = structs.Services{
|
expected = structs.Services{
|
||||||
"redis": []string{"master", "prod", "slave"},
|
"redis": []string{"master", "prod", "slave"},
|
||||||
}
|
}
|
||||||
@ -1006,6 +985,29 @@ func TestStateStore_ServicesByNodeMeta(t *testing.T) {
|
|||||||
if !reflect.DeepEqual(res, expected) {
|
if !reflect.DeepEqual(res, expected) {
|
||||||
t.Fatalf("bad: %v %v", 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) {
|
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
|
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`.
|
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:
|
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
|
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`.
|
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:
|
It returns a JSON body like this:
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user