diff --git a/pkg/config/config.go b/pkg/config/config.go index afeba8cd..55c74a47 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -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. diff --git a/pkg/naming/metric_namer.go b/pkg/naming/metric_namer.go index 1d160bdc..3e47049b 100644 --- a/pkg/naming/metric_namer.go +++ b/pkg/naming/metric_namer.go @@ -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 diff --git a/pkg/naming/metrics_query.go b/pkg/naming/metrics_query.go index 30411863..4bf8c5dd 100644 --- a/pkg/naming/metrics_query.go +++ b/pkg/naming/metrics_query.go @@ -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 diff --git a/pkg/naming/metrics_query_test.go b/pkg/naming/metrics_query_test.go index 8ba6a0be..71d27ce0 100644 --- a/pkg/naming/metrics_query_test.go +++ b/pkg/naming/metrics_query_test.go @@ -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 {