Update metrics apiserver to support filtering by labels

This commit is contained in:
Sergii Koshel 2020-02-12 17:54:40 +02:00
parent 03bc47e9fb
commit d091fff18b
11 changed files with 331 additions and 187 deletions

View file

@ -45,7 +45,7 @@ type MetricNamer interface {
MetricNameForSeries(series prom.Series) (string, error)
// QueryForSeries returns the query for a given series (not API metric name), with
// the given namespace name (if relevant), resource, and resource names.
QueryForSeries(series string, resource schema.GroupResource, namespace string, names ...string) (prom.Selector, error)
QueryForSeries(series string, resource schema.GroupResource, namespace string, metricSelector labels.Selector, names ...string) (prom.Selector, error)
// QueryForExternalSeries returns the query for a given series (not API metric name), with
// the given namespace name (if relevant), resource, and resource names.
QueryForExternalSeries(series string, namespace string, targetLabels labels.Selector) (prom.Selector, error)
@ -126,8 +126,8 @@ SeriesLoop:
return finalSeries
}
func (n *metricNamer) QueryForSeries(series string, resource schema.GroupResource, namespace string, names ...string) (prom.Selector, error) {
return n.metricsQuery.Build(series, resource, namespace, nil, names...)
func (n *metricNamer) QueryForSeries(series string, resource schema.GroupResource, namespace string, metricSelector labels.Selector, names ...string) (prom.Selector, error) {
return n.metricsQuery.Build(series, resource, namespace, nil, metricSelector, names...)
}
func (n *metricNamer) QueryForExternalSeries(series string, namespace string, metricSelector labels.Selector) (prom.Selector, error) {

View file

@ -39,7 +39,7 @@ type MetricsQuery interface {
// is considered to be root-scoped. extraGroupBy may be used for cases
// where we need to scope down more specifically than just the group-resource
// (e.g. container metrics).
Build(series string, groupRes schema.GroupResource, namespace string, extraGroupBy []string, resourceNames ...string) (prom.Selector, error)
Build(series string, groupRes schema.GroupResource, namespace string, extraGroupBy []string, metricSelector labels.Selector, resourceNames ...string) (prom.Selector, error)
BuildExternal(seriesName string, namespace string, groupBy string, groupBySlice []string, metricSelector labels.Selector) (prom.Selector, error)
}
@ -85,17 +85,25 @@ type queryPart struct {
operator selection.Operator
}
func (q *metricsQuery) Build(series string, resource schema.GroupResource, namespace string, extraGroupBy []string, names ...string) (prom.Selector, error) {
var exprs []string
valuesByName := map[string]string{}
func (q *metricsQuery) Build(series string, resource schema.GroupResource, namespace string, extraGroupBy []string, metricSelector labels.Selector, names ...string) (prom.Selector, error) {
queryParts := q.createQueryPartsFromSelector(metricSelector)
if namespace != "" {
namespaceLbl, err := q.resConverter.LabelForResource(NsGroupResource)
if err != nil {
return "", err
}
exprs = append(exprs, prom.LabelEq(string(namespaceLbl), namespace))
valuesByName[string(namespaceLbl)] = namespace
queryParts = append(queryParts, queryPart{
labelName: string(namespaceLbl),
values: []string{namespace},
operator: selection.Equals,
})
}
exprs, valuesByName, err := q.processQueryParts(queryParts)
if err != nil {
return "", err
}
resourceLbl, err := q.resConverter.LabelForResource(resource)

View file

@ -83,23 +83,33 @@ func TestBuildSelector(t *testing.T) {
return mq
}
mustNewLabelRequirement := func(key string, op selection.Operator, vals []string) *labels.Requirement {
req, err := labels.NewRequirement(key, op, vals)
if err != nil {
t.Fatal(err)
}
return req
}
tests := []struct {
name string
mq MetricsQuery
series string
resource schema.GroupResource
namespace string
extraGroupBy []string
names []string
series string
resource schema.GroupResource
namespace string
extraGroupBy []string
metricSelector labels.Selector
names []string
check checkFunc
}{
{
name: "series",
mq: mustNewQuery(`series <<.Series>>`, false),
series: "foo",
mq: mustNewQuery(`series <<.Series>>`, false),
metricSelector: labels.NewSelector(),
series: "foo",
check: checks(
hasError(nil),
@ -110,9 +120,10 @@ func TestBuildSelector(t *testing.T) {
{
name: "multiple LabelMatchers values",
mq: mustNewQuery(`<<.LabelMatchers>>`, false),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
names: []string{"bar", "baz"},
mq: mustNewQuery(`<<.LabelMatchers>>`, false),
metricSelector: labels.NewSelector(),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
names: []string{"bar", "baz"},
check: checks(
hasError(nil),
@ -123,9 +134,10 @@ func TestBuildSelector(t *testing.T) {
{
name: "single LabelMatchers value",
mq: mustNewQuery(`<<.LabelMatchers>>`, false),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
names: []string{"bar"},
mq: mustNewQuery(`<<.LabelMatchers>>`, false),
metricSelector: labels.NewSelector(),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
names: []string{"bar"},
check: checks(
hasError(nil),
@ -134,12 +146,29 @@ func TestBuildSelector(t *testing.T) {
},
{
name: "single LabelValuesByName value",
name: "LabelMatchers with additional metrics filter",
mq: mustNewQuery(`<<index .LabelValuesByName "resource">>`, false),
mq: mustNewQuery(`<<.LabelMatchers>>`, false),
metricSelector: labels.NewSelector().Add(
*mustNewLabelRequirement("metric1", selection.Equals, []string{"value1"}),
),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
names: []string{"bar"},
check: checks(
hasError(nil),
hasSelector(`metric1="value1",resource="bar"`),
),
},
{
name: "single LabelValuesByName value",
mq: mustNewQuery(`<<index .LabelValuesByName "resource">>`, false),
metricSelector: labels.NewSelector(),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
names: []string{"bar"},
check: checks(
hasError(nil),
hasSelector("bar"),
@ -149,9 +178,10 @@ func TestBuildSelector(t *testing.T) {
{
name: "multiple LabelValuesByName values",
mq: mustNewQuery(`<<index .LabelValuesByName "resource">>`, false),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
names: []string{"bar", "baz"},
mq: mustNewQuery(`<<index .LabelValuesByName "resource">>`, false),
metricSelector: labels.NewSelector(),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
names: []string{"bar", "baz"},
check: checks(
hasError(nil),
@ -162,10 +192,11 @@ func TestBuildSelector(t *testing.T) {
{
name: "multiple LabelValuesByName values with namespace",
mq: mustNewQuery(`<<index .LabelValuesByName "namespaces">> <<index .LabelValuesByName "resource">>`, true),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
namespace: "default",
names: []string{"bar", "baz"},
mq: mustNewQuery(`<<index .LabelValuesByName "namespaces">> <<index .LabelValuesByName "resource">>`, true),
metricSelector: labels.NewSelector(),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
namespace: "default",
names: []string{"bar", "baz"},
check: checks(
hasError(nil),
@ -176,8 +207,9 @@ func TestBuildSelector(t *testing.T) {
{
name: "single GroupBy value",
mq: mustNewQuery(`<<.GroupBy>>`, false),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
mq: mustNewQuery(`<<.GroupBy>>`, false),
metricSelector: labels.NewSelector(),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
check: checks(
hasError(nil),
@ -188,9 +220,10 @@ func TestBuildSelector(t *testing.T) {
{
name: "multiple GroupBy values",
mq: mustNewQuery(`<<.GroupBy>>`, false),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
extraGroupBy: []string{"extra", "groups"},
mq: mustNewQuery(`<<.GroupBy>>`, false),
metricSelector: labels.NewSelector(),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
extraGroupBy: []string{"extra", "groups"},
check: checks(
hasError(nil),
@ -201,8 +234,9 @@ func TestBuildSelector(t *testing.T) {
{
name: "single GroupBySlice value",
mq: mustNewQuery(`<<.GroupBySlice>>`, false),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
mq: mustNewQuery(`<<.GroupBySlice>>`, false),
metricSelector: labels.NewSelector(),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
check: checks(
hasError(nil),
@ -213,9 +247,10 @@ func TestBuildSelector(t *testing.T) {
{
name: "multiple GroupBySlice values",
mq: mustNewQuery(`<<.GroupBySlice>>`, false),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
extraGroupBy: []string{"extra", "groups"},
mq: mustNewQuery(`<<.GroupBySlice>>`, false),
metricSelector: labels.NewSelector(),
resource: schema.GroupResource{Group: "group", Resource: "resource"},
extraGroupBy: []string{"extra", "groups"},
check: checks(
hasError(nil),
@ -226,7 +261,7 @@ func TestBuildSelector(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
selector, err := tc.mq.Build(tc.series, tc.resource, tc.namespace, tc.extraGroupBy, tc.names...)
selector, err := tc.mq.Build(tc.series, tc.resource, tc.namespace, tc.extraGroupBy, tc.metricSelector, tc.names...)
if err := tc.check(selector, err); err != nil {
t.Error(err)