From 5e342d315c7e191427e7ec1570cefc094c99a922 Mon Sep 17 00:00:00 2001 From: Mohammad Varmazyar Date: Fri, 17 Oct 2025 00:29:36 +0200 Subject: [PATCH] discovery/consul: Fix filter parameter not applied to health endpoint The filter parameter was only being passed to catalog.Services() but not to health.ServiceMultipleTags(), causing filters on Node and Node.Meta to be ignored when discovering service instances. This adds the missing Filter field to QueryOptions in the consulService.watch() method. Fixes #16087 Signed-off-by: Mohammad Varmazyar Signed-off-by: Mohammad Varmazyar --- CHANGELOG.md | 2 ++ discovery/consul/consul.go | 1 + discovery/consul/consul_test.go | 50 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 649196758b..d35bfce65d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## main / unreleased +* [BUGFIX] Discovery/Consul: Fix filter parameter not being applied to health service endpoint, causing Node and Node.Meta filters to be ignored. #16087 + ## 3.7.0 / 2025-10-15 * [CHANGE] Remote-write: the following metrics are deprecated: diff --git a/discovery/consul/consul.go b/discovery/consul/consul.go index 74b5d0724e..600bd274a4 100644 --- a/discovery/consul/consul.go +++ b/discovery/consul/consul.go @@ -499,6 +499,7 @@ func (srv *consulService) watch(ctx context.Context, ch chan<- []*targetgroup.Gr WaitTime: watchTimeout, AllowStale: srv.discovery.allowStale, NodeMeta: srv.discovery.watchedNodeMeta, + Filter: srv.discovery.watchedFilter, } t0 := time.Now() diff --git a/discovery/consul/consul_test.go b/discovery/consul/consul_test.go index a6ff4a625e..b813146089 100644 --- a/discovery/consul/consul_test.go +++ b/discovery/consul/consul_test.go @@ -240,6 +240,8 @@ func newServer(t *testing.T) (*httptest.Server, *SDConfig) { response = ServiceTestAnswer case "/v1/health/service/test?wait=120000ms": response = ServiceTestAnswer + case "/v1/health/service/test?filter=NodeMeta.rack_name+%3D%3D+%222304%22&wait=120000ms": + response = ServiceTestAnswer case "/v1/health/service/other?wait=120000ms": response = `[]` case "/v1/catalog/services?node-meta=rack_name%3A2304&stale=&wait=120000ms": @@ -392,6 +394,54 @@ func TestFilterOption(t *testing.T) { cancel() } +// TestFilterOnHealthEndpoint verifies that filter is passed to health service endpoint. +func TestFilterOnHealthEndpoint(t *testing.T) { + filterReceived := false + stub := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + response := "" + switch r.URL.Path { + case "/v1/agent/self": + response = AgentAnswer + case "/v1/health/service/test": + // Verify filter parameter is present in the query + filter := r.URL.Query().Get("filter") + if filter == `Node.Meta.rack_name == "2304"` { + filterReceived = true + } + response = ServiceTestAnswer + default: + t.Errorf("Unhandled consul call: %s", r.URL) + } + w.Header().Add("X-Consul-Index", "1") + w.Write([]byte(response)) + })) + defer stub.Close() + + stuburl, err := url.Parse(stub.URL) + require.NoError(t, err) + + config := &SDConfig{ + Server: stuburl.Host, + Services: []string{"test"}, + Filter: `Node.Meta.rack_name == "2304"`, + RefreshInterval: model.Duration(1 * time.Second), + } + + 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() + + // Verify the filter was actually sent to the health endpoint + require.True(t, filterReceived, "Filter parameter should be sent to health service endpoint") +} + func TestGetDatacenterShouldReturnError(t *testing.T) { for _, tc := range []struct { handler func(http.ResponseWriter, *http.Request)