diff --git a/pkg/promlib/models/scope.go b/pkg/promlib/models/scope.go index 3fd7a62b8eb..41fbbb43444 100644 --- a/pkg/promlib/models/scope.go +++ b/pkg/promlib/models/scope.go @@ -73,11 +73,31 @@ func ApplyFiltersAndGroupBy(rawExpr string, scopeFilters, adHocFilters []ScopeFi func FiltersToMatchers(scopeFilters, adhocFilters []ScopeFilter) ([]*labels.Matcher, error) { filterMap := make(map[string]*labels.Matcher) - for _, filter := range append(scopeFilters, adhocFilters...) { + // scope filters are applied first + for _, filter := range scopeFilters { matcher, err := filterToMatcher(filter) if err != nil { return nil, err } + + // when scopes have the same key, both values should be matched + // in prometheus that means using an regex with both values + if _, ok := filterMap[filter.Key]; ok { + filterMap[filter.Key].Value = filterMap[filter.Key].Value + "|" + matcher.Value + filterMap[filter.Key].Type = labels.MatchRegexp + } else { + filterMap[filter.Key] = matcher + } + } + + // ad hoc filters are applied after scope filters + for _, filter := range adhocFilters { + matcher, err := filterToMatcher(filter) + if err != nil { + return nil, err + } + + // when ad hoc filters have the same key, the last one should be used filterMap[filter.Key] = matcher } diff --git a/pkg/promlib/models/scope_test.go b/pkg/promlib/models/scope_test.go index 4799b148835..fc7d972eb44 100644 --- a/pkg/promlib/models/scope_test.go +++ b/pkg/promlib/models/scope_test.go @@ -106,6 +106,29 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { expected: `{__name__="http_requests_total",namespace="istio"}`, expectErr: false, }, + { + name: "merge scopes filters into using OR if they share filter key", + query: `http_requests_total{}`, + scopeFilters: []ScopeFilter{ + {Key: "namespace", Value: "default", Operator: FilterOperatorEquals}, + {Key: "namespace", Value: "kube-system", Operator: FilterOperatorEquals}, + }, + expected: `http_requests_total{namespace=~"default|kube-system"}`, + expectErr: false, + }, + { + name: "adhoc filters win over scope filters if they share filter key", + query: `http_requests_total{}`, + scopeFilters: []ScopeFilter{ + {Key: "namespace", Value: "default", Operator: FilterOperatorEquals}, + {Key: "namespace", Value: "kube-system", Operator: FilterOperatorEquals}, + }, + adhocFilters: []ScopeFilter{ + {Key: "namespace", Value: "adhoc-wins", Operator: FilterOperatorEquals}, + }, + expected: `http_requests_total{namespace="adhoc-wins"}`, + expectErr: false, + }, } for _, tt := range tests { @@ -116,7 +139,7 @@ func TestApplyQueryFiltersAndGroupBy_Filters(t *testing.T) { require.Error(t, err) } else { require.NoError(t, err) - require.Equal(t, tt.expected, expr) + require.Equal(t, tt.expected, expr, tt.name) } }) }