mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-27 06:29:42 -08:00
consul: Add support for catalog list services filter
This adds support for Consul's Catalog [List Services][^1] API's `filter` parameter added in 1.14.x. This parameter grants the operator more flexibility to do server-side filtering of the Catalog, before Prometheus subscribes for updates. Operators can use this to improve both the performance of Prometheus's Consul SD and reduce the impact of enumerating large catalogs. [^1]: https://developer.hashicorp.com/consul/api-docs/v1.14.x/catalog Signed-off-by: Daniel Kimsey <dekimsey@protonmail.com>
This commit is contained in:
parent
a0e93e403e
commit
aa3e58358b
|
@ -113,8 +113,11 @@ type SDConfig struct {
|
||||||
Services []string `yaml:"services,omitempty"`
|
Services []string `yaml:"services,omitempty"`
|
||||||
// A list of tags used to filter instances inside a service. Services must contain all tags in the list.
|
// A list of tags used to filter instances inside a service. Services must contain all tags in the list.
|
||||||
ServiceTags []string `yaml:"tags,omitempty"`
|
ServiceTags []string `yaml:"tags,omitempty"`
|
||||||
// Desired node metadata.
|
// Desired node metadata. As of Consul 1.14, consider `filter` instead.
|
||||||
NodeMeta map[string]string `yaml:"node_meta,omitempty"`
|
NodeMeta map[string]string `yaml:"node_meta,omitempty"`
|
||||||
|
// Consul filter string
|
||||||
|
// See https://www.consul.io/api-docs/catalog#filtering-1, for syntax
|
||||||
|
Filter string `yaml:"filter,omitempty"`
|
||||||
|
|
||||||
HTTPClientConfig config.HTTPClientConfig `yaml:",inline"`
|
HTTPClientConfig config.HTTPClientConfig `yaml:",inline"`
|
||||||
}
|
}
|
||||||
|
@ -174,6 +177,7 @@ type Discovery struct {
|
||||||
watchedServices []string // Set of services which will be discovered.
|
watchedServices []string // Set of services which will be discovered.
|
||||||
watchedTags []string // Tags used to filter instances of a service.
|
watchedTags []string // Tags used to filter instances of a service.
|
||||||
watchedNodeMeta map[string]string
|
watchedNodeMeta map[string]string
|
||||||
|
watchedFilter string
|
||||||
allowStale bool
|
allowStale bool
|
||||||
refreshInterval time.Duration
|
refreshInterval time.Duration
|
||||||
finalizer func()
|
finalizer func()
|
||||||
|
@ -218,6 +222,7 @@ func NewDiscovery(conf *SDConfig, logger log.Logger, metrics discovery.Discovere
|
||||||
watchedServices: conf.Services,
|
watchedServices: conf.Services,
|
||||||
watchedTags: conf.ServiceTags,
|
watchedTags: conf.ServiceTags,
|
||||||
watchedNodeMeta: conf.NodeMeta,
|
watchedNodeMeta: conf.NodeMeta,
|
||||||
|
watchedFilter: conf.Filter,
|
||||||
allowStale: conf.AllowStale,
|
allowStale: conf.AllowStale,
|
||||||
refreshInterval: time.Duration(conf.RefreshInterval),
|
refreshInterval: time.Duration(conf.RefreshInterval),
|
||||||
clientDatacenter: conf.Datacenter,
|
clientDatacenter: conf.Datacenter,
|
||||||
|
@ -361,13 +366,14 @@ func (d *Discovery) Run(ctx context.Context, ch chan<- []*targetgroup.Group) {
|
||||||
// entire list of services.
|
// entire list of services.
|
||||||
func (d *Discovery) watchServices(ctx context.Context, ch chan<- []*targetgroup.Group, lastIndex *uint64, services map[string]func()) {
|
func (d *Discovery) watchServices(ctx context.Context, ch chan<- []*targetgroup.Group, lastIndex *uint64, services map[string]func()) {
|
||||||
catalog := d.client.Catalog()
|
catalog := d.client.Catalog()
|
||||||
level.Debug(d.logger).Log("msg", "Watching services", "tags", strings.Join(d.watchedTags, ","))
|
level.Debug(d.logger).Log("msg", "Watching services", "tags", strings.Join(d.watchedTags, ","), "filter", d.watchedFilter)
|
||||||
|
|
||||||
opts := &consul.QueryOptions{
|
opts := &consul.QueryOptions{
|
||||||
WaitIndex: *lastIndex,
|
WaitIndex: *lastIndex,
|
||||||
WaitTime: watchTimeout,
|
WaitTime: watchTimeout,
|
||||||
AllowStale: d.allowStale,
|
AllowStale: d.allowStale,
|
||||||
NodeMeta: d.watchedNodeMeta,
|
NodeMeta: d.watchedNodeMeta,
|
||||||
|
Filter: d.watchedFilter,
|
||||||
}
|
}
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
srvs, meta, err := catalog.Services(opts.WithContext(ctx))
|
srvs, meta, err := catalog.Services(opts.WithContext(ctx))
|
||||||
|
|
|
@ -252,6 +252,8 @@ func newServer(t *testing.T) (*httptest.Server, *SDConfig) {
|
||||||
case "/v1/catalog/services?index=1&wait=120000ms":
|
case "/v1/catalog/services?index=1&wait=120000ms":
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
response = ServicesTestAnswer
|
response = ServicesTestAnswer
|
||||||
|
case "/v1/catalog/services?filter=NodeMeta.rack_name+%3D%3D+%222304%22&index=1&wait=120000ms":
|
||||||
|
response = ServicesTestAnswer
|
||||||
default:
|
default:
|
||||||
t.Errorf("Unhandled consul call: %s", r.URL)
|
t.Errorf("Unhandled consul call: %s", r.URL)
|
||||||
}
|
}
|
||||||
|
@ -369,6 +371,27 @@ func TestAllOptions(t *testing.T) {
|
||||||
<-ch
|
<-ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Watch the test service with a specific tag and node-meta via Filter parameter.
|
||||||
|
func TestFilterOption(t *testing.T) {
|
||||||
|
stub, config := newServer(t)
|
||||||
|
defer stub.Close()
|
||||||
|
|
||||||
|
config.Services = []string{"test"}
|
||||||
|
config.Filter = `NodeMeta.rack_name == "2304"`
|
||||||
|
config.Token = "fake-token"
|
||||||
|
|
||||||
|
d := newDiscovery(t, config)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
ch := make(chan []*targetgroup.Group)
|
||||||
|
go func() {
|
||||||
|
d.Run(ctx, ch)
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
checkOneTarget(t, <-ch)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetDatacenterShouldReturnError(t *testing.T) {
|
func TestGetDatacenterShouldReturnError(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
handler func(http.ResponseWriter, *http.Request)
|
handler func(http.ResponseWriter, *http.Request)
|
||||||
|
|
|
@ -716,14 +716,17 @@ The following meta labels are available on targets during [relabeling](#relabel_
|
||||||
services:
|
services:
|
||||||
[ - <string> ]
|
[ - <string> ]
|
||||||
|
|
||||||
# See https://www.consul.io/api/catalog.html#list-nodes-for-service to know more
|
# A Consul Filter expression used to filter the catalog results
|
||||||
# about the possible filters that can be used.
|
# See https://www.consul.io/api-docs/catalog#list-services to know more
|
||||||
|
# about the filter expressions that can be used.
|
||||||
|
[ filter: <string> ]
|
||||||
|
|
||||||
|
# The `tags` and `node_meta` fields are deprecated in Consul in favor of `filter`.
|
||||||
# An optional list of tags used to filter nodes for a given service. Services must contain all tags in the list.
|
# An optional list of tags used to filter nodes for a given service. Services must contain all tags in the list.
|
||||||
tags:
|
tags:
|
||||||
[ - <string> ]
|
[ - <string> ]
|
||||||
|
|
||||||
# Node metadata key/value pairs to filter nodes for a given service.
|
# Node metadata key/value pairs to filter nodes for a given service. As of Consul 1.14, consider `filter` instead.
|
||||||
[ node_meta:
|
[ node_meta:
|
||||||
[ <string>: <string> ... ] ]
|
[ <string>: <string> ... ] ]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue