Adds Support for Cross-Namespace Metrics

This commit is contained in:
AshMenhennett 2019-11-01 10:28:30 +11:00
parent 03bc47e9fb
commit 718cb2400e
4 changed files with 51 additions and 5 deletions

View file

@ -38,6 +38,9 @@ type DiscoveryRule struct {
// `.GroupBy` is the comma-separated expected group-by label names. The delimeters
// are `<<` and `>>`.
MetricsQuery string `yaml:"metricsQuery,omitempty"`
// Options specifies additional parameters that are taken into account
// when configuring a rule. These can be used to fine-tune the retrieval of metrics
Options Options `yaml:"options,omitempty"`
}
// RegexFilter is a filter that matches positively or negatively against a regex.
@ -80,6 +83,13 @@ type NameMapping struct {
As string `yaml:"as"`
}
// Option describes a set of customisations for retrieving metrics
type Options struct {
// option to allow cross-namespace metrics. Used to determine whether to
// append the namespace as a label selector when retrieving metrics
DetatchFromNamespace bool `yaml:"detachFromNamespace,omitempty"`
}
// ResourceRules describe the rules for querying resource metrics
// API results. It's assumed that the same metrics can be used
// to aggregate across different resources.

View file

@ -104,6 +104,8 @@ type metricNamer struct {
seriesMatchers []*ReMatcher
ResourceConverter
options config.Options
}
// queryTemplateArgs are the arguments for the metrics query template.
@ -133,7 +135,7 @@ func (n *metricNamer) QueryForSeries(series string, resource schema.GroupResourc
func (n *metricNamer) QueryForExternalSeries(series string, namespace string, metricSelector labels.Selector) (prom.Selector, error) {
//test := prom.Selector()
//return test, nil
return n.metricsQuery.BuildExternal(series, namespace, "", []string{}, metricSelector)
return n.metricsQuery.BuildExternal(series, namespace, "", []string{}, metricSelector, n.options)
}
func (n *metricNamer) MetricNameForSeries(series prom.Series) (string, error) {
@ -208,6 +210,7 @@ func NamersFromConfig(cfg []config.DiscoveryRule, mapper apimeta.RESTMapper) ([]
nameAs: nameAs,
seriesMatchers: seriesMatchers,
ResourceConverter: resConv,
options: rule.Options,
}
namers[i] = namer

View file

@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/selection"
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
"github.com/directxman12/k8s-prometheus-adapter/pkg/config"
)
// MetricsQuery represents a compiled metrics query for some set of
@ -40,7 +41,7 @@ type MetricsQuery interface {
// 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)
BuildExternal(seriesName string, namespace string, groupBy string, groupBySlice []string, metricSelector labels.Selector) (prom.Selector, error)
BuildExternal(seriesName string, namespace string, groupBy string, groupBySlice []string, metricSelector labels.Selector, options config.Options) (prom.Selector, error)
}
// NewMetricsQuery constructs a new MetricsQuery by compiling the given Go template.
@ -136,13 +137,13 @@ func (q *metricsQuery) Build(series string, resource schema.GroupResource, names
return prom.Selector(queryBuff.String()), nil
}
func (q *metricsQuery) BuildExternal(seriesName string, namespace string, groupBy string, groupBySlice []string, metricSelector labels.Selector) (prom.Selector, error) {
func (q *metricsQuery) BuildExternal(seriesName string, namespace string, groupBy string, groupBySlice []string, metricSelector labels.Selector, options config.Options) (prom.Selector, error) {
queryParts := []queryPart{}
// Build up the query parts from the selector.
queryParts = append(queryParts, q.createQueryPartsFromSelector(metricSelector)...)
if namespace != "" {
if namespace != "" && options.DetatchFromNamespace != true {
namespaceLbl, err := q.resConverter.LabelForResource(NsGroupResource)
if err != nil {
return "", err

View file

@ -21,6 +21,7 @@ import (
"testing"
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
"github.com/directxman12/k8s-prometheus-adapter/pkg/config"
pmodel "github.com/prometheus/common/model"
labels "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -261,6 +262,7 @@ func TestBuildExternalSelector(t *testing.T) {
groupBy string
groupBySlice []string
metricSelector labels.Selector
options config.Options
check checkFunc
}{
@ -340,6 +342,36 @@ func TestBuildExternalSelector(t *testing.T) {
hasSelector(`foo="bar",namespaces="default"`),
),
},
{
name: "single LabelMatchers value with namespace - detached namespace",
mq: mustNewQuery(`<<.LabelMatchers>>`),
namespace: "default",
options: config.Options{DetatchFromNamespace: true},
metricSelector: labels.NewSelector().Add(
*mustNewLabelRequirement("foo", selection.Equals, []string{"bar"}),
),
check: checks(
hasError(nil),
hasSelector(`foo="bar"`),
),
},
{
name: "single LabelMatchers value with namespace - explicit attached namespace",
mq: mustNewQuery(`<<.LabelMatchers>>`),
namespace: "default",
options: config.Options{DetatchFromNamespace: false},
metricSelector: labels.NewSelector().Add(
*mustNewLabelRequirement("foo", selection.Equals, []string{"bar"}),
),
check: checks(
hasError(nil),
hasSelector(`foo="bar",namespaces="default"`),
),
},
{
name: "multiple LabelMatchers value",
@ -384,7 +416,7 @@ func TestBuildExternalSelector(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
selector, err := tc.mq.BuildExternal(tc.series, tc.namespace, tc.groupBy, tc.groupBySlice, tc.metricSelector)
selector, err := tc.mq.BuildExternal(tc.series, tc.namespace, tc.groupBy, tc.groupBySlice, tc.metricSelector, tc.options)
t.Logf("selector: '%v'", selector)
if err := tc.check(selector, err); err != nil {