mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 01:38:10 +00:00
Refactor metrics query building to interface
This moves the metrics query building to a separate interface in the naming package so that it can be used across providers.
This commit is contained in:
parent
7dd9e94aea
commit
74c0c53e4f
2 changed files with 128 additions and 70 deletions
|
|
@ -1,11 +1,9 @@
|
||||||
package provider
|
package provider
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
|
||||||
|
|
||||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
@ -84,24 +82,16 @@ func (m *reMatcher) Matches(val string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type metricNamer struct {
|
type metricNamer struct {
|
||||||
seriesQuery prom.Selector
|
seriesQuery prom.Selector
|
||||||
metricsQueryTemplate *template.Template
|
metricsQuery naming.MetricsQuery
|
||||||
nameMatches *regexp.Regexp
|
nameMatches *regexp.Regexp
|
||||||
nameAs string
|
nameAs string
|
||||||
seriesMatchers []*reMatcher
|
seriesMatchers []*reMatcher
|
||||||
|
|
||||||
naming.ResourceConverter
|
naming.ResourceConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
// queryTemplateArgs are the arguments for the metrics query template.
|
// queryTemplateArgs are the arguments for the metrics query template.
|
||||||
type queryTemplateArgs struct {
|
|
||||||
Series string
|
|
||||||
LabelMatchers string
|
|
||||||
LabelValuesByName map[string][]string
|
|
||||||
GroupBy string
|
|
||||||
GroupBySlice []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *metricNamer) FilterSeries(initialSeries []prom.Series) []prom.Series {
|
func (n *metricNamer) FilterSeries(initialSeries []prom.Series) []prom.Series {
|
||||||
if len(n.seriesMatchers) == 0 {
|
if len(n.seriesMatchers) == 0 {
|
||||||
return initialSeries
|
return initialSeries
|
||||||
|
|
@ -122,48 +112,7 @@ SeriesLoop:
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *metricNamer) QueryForSeries(series string, resource schema.GroupResource, namespace string, names ...string) (prom.Selector, error) {
|
func (n *metricNamer) QueryForSeries(series string, resource schema.GroupResource, namespace string, names ...string) (prom.Selector, error) {
|
||||||
var exprs []string
|
return n.metricsQuery.Build(series, resource, namespace, nil, names...)
|
||||||
valuesByName := map[string][]string{}
|
|
||||||
|
|
||||||
if namespace != "" {
|
|
||||||
namespaceLbl, err := n.LabelForResource(nsGroupResource)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
exprs = append(exprs, prom.LabelEq(string(namespaceLbl), namespace))
|
|
||||||
valuesByName[string(namespaceLbl)] = []string{namespace}
|
|
||||||
}
|
|
||||||
|
|
||||||
resourceLbl, err := n.LabelForResource(resource)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
matcher := prom.LabelEq
|
|
||||||
targetValue := names[0]
|
|
||||||
if len(names) > 1 {
|
|
||||||
matcher = prom.LabelMatches
|
|
||||||
targetValue = strings.Join(names, "|")
|
|
||||||
}
|
|
||||||
exprs = append(exprs, matcher(string(resourceLbl), targetValue))
|
|
||||||
valuesByName[string(resourceLbl)] = names
|
|
||||||
|
|
||||||
args := queryTemplateArgs{
|
|
||||||
Series: series,
|
|
||||||
LabelMatchers: strings.Join(exprs, ","),
|
|
||||||
LabelValuesByName: valuesByName,
|
|
||||||
GroupBy: string(resourceLbl),
|
|
||||||
GroupBySlice: []string{string(resourceLbl)},
|
|
||||||
}
|
|
||||||
queryBuff := new(bytes.Buffer)
|
|
||||||
if err := n.metricsQueryTemplate.Execute(queryBuff, args); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if queryBuff.Len() == 0 {
|
|
||||||
return "", fmt.Errorf("empty query produced by metrics query template")
|
|
||||||
}
|
|
||||||
|
|
||||||
return prom.Selector(queryBuff.String()), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *metricNamer) MetricNameForSeries(series prom.Series) (string, error) {
|
func (n *metricNamer) MetricNameForSeries(series prom.Series) (string, error) {
|
||||||
|
|
@ -180,9 +129,14 @@ func NamersFromConfig(cfg *config.MetricsDiscoveryConfig, mapper apimeta.RESTMap
|
||||||
namers := make([]MetricNamer, len(cfg.Rules))
|
namers := make([]MetricNamer, len(cfg.Rules))
|
||||||
|
|
||||||
for i, rule := range cfg.Rules {
|
for i, rule := range cfg.Rules {
|
||||||
metricsQueryTemplate, err := template.New("metrics-query").Delims("<<", ">>").Parse(rule.MetricsQuery)
|
resConv, err := naming.NewResourceConverter(rule.Resources.Template, rule.Resources.Overrides, mapper)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to parse metrics query template %q associated with series query %q: %v", rule.MetricsQuery, rule.SeriesQuery, err)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsQuery, err := naming.NewMetricsQuery(rule.MetricsQuery, resConv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to construct metrics query associated with series query %q: %v", rule.SeriesQuery, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
seriesMatchers := make([]*reMatcher, len(rule.SeriesFilters))
|
seriesMatchers := make([]*reMatcher, len(rule.SeriesFilters))
|
||||||
|
|
@ -226,18 +180,13 @@ func NamersFromConfig(cfg *config.MetricsDiscoveryConfig, mapper apimeta.RESTMap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resConv, err := naming.NewResourceConverter(rule.Resources.Template, rule.Resources.Overrides, mapper)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
namer := &metricNamer{
|
namer := &metricNamer{
|
||||||
seriesQuery: prom.Selector(rule.SeriesQuery),
|
seriesQuery: prom.Selector(rule.SeriesQuery),
|
||||||
metricsQueryTemplate: metricsQueryTemplate,
|
metricsQuery: metricsQuery,
|
||||||
nameMatches: nameMatches,
|
nameMatches: nameMatches,
|
||||||
nameAs: nameAs,
|
nameAs: nameAs,
|
||||||
seriesMatchers: seriesMatchers,
|
seriesMatchers: seriesMatchers,
|
||||||
ResourceConverter: resConv,
|
ResourceConverter: resConv,
|
||||||
}
|
}
|
||||||
|
|
||||||
namers[i] = namer
|
namers[i] = namer
|
||||||
|
|
|
||||||
109
pkg/naming/metrics_query.go
Normal file
109
pkg/naming/metrics_query.go
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
package naming
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
|
||||||
|
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MetricsQuery represents a compiled metrics query for some set of
|
||||||
|
// series that can be converted into an series of Prometheus expressions to
|
||||||
|
// be passed to a client.
|
||||||
|
type MetricsQuery interface {
|
||||||
|
// Build constructs Prometheus expressions to represent this query
|
||||||
|
// over the given group-resource. If namespace is empty, the resource
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetricsQuery constructs a new MetricsQuery by compiling the given Go template.
|
||||||
|
// The delimiters on the template are `<<` and `>>`, and it may use the following fields:
|
||||||
|
// - Series: the series in question
|
||||||
|
// - LabelMatchers: a pre-stringified form of the label matchers for the resources in the query
|
||||||
|
// - LabelMatchersByName: the raw map-form of the above matchers
|
||||||
|
// - GroupBy: the group-by clause to use for the resources in the query (stringified)
|
||||||
|
// - GroupBySlice: the raw slice form of the above group-by clause
|
||||||
|
func NewMetricsQuery(queryTemplate string, resourceConverter ResourceConverter) (MetricsQuery, error) {
|
||||||
|
templ, err := template.New("metrics-query").Delims("<<", ">>").Parse(queryTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse metrics query template %q: %v", queryTemplate, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &metricsQuery{
|
||||||
|
resConverter: resourceConverter,
|
||||||
|
template: templ,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// metricsQuery is a MetricsQuery based on a compiled Go text template.
|
||||||
|
// with the delimiters as `<<` and `>>`, and the arguments found in
|
||||||
|
// queryTemplateArgs.
|
||||||
|
type metricsQuery struct {
|
||||||
|
resConverter ResourceConverter
|
||||||
|
template *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryTemplateArgs contains the arguments for the template used in metricsQuery.
|
||||||
|
type queryTemplateArgs struct {
|
||||||
|
Series string
|
||||||
|
LabelMatchers string
|
||||||
|
LabelValuesByName map[string][]string
|
||||||
|
GroupBy string
|
||||||
|
GroupBySlice []string
|
||||||
|
}
|
||||||
|
|
||||||
|
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{}
|
||||||
|
|
||||||
|
if namespace != "" {
|
||||||
|
namespaceLbl, err := q.resConverter.LabelForResource(nsGroupResource)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
exprs = append(exprs, prom.LabelEq(string(namespaceLbl), namespace))
|
||||||
|
valuesByName[string(namespaceLbl)] = []string{namespace}
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceLbl, err := q.resConverter.LabelForResource(resource)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
matcher := prom.LabelEq
|
||||||
|
targetValue := names[0]
|
||||||
|
if len(names) > 1 {
|
||||||
|
matcher = prom.LabelMatches
|
||||||
|
targetValue = strings.Join(names, "|")
|
||||||
|
}
|
||||||
|
exprs = append(exprs, matcher(string(resourceLbl), targetValue))
|
||||||
|
valuesByName[string(resourceLbl)] = names
|
||||||
|
|
||||||
|
groupBy := make([]string, 0, len(extraGroupBy)+1)
|
||||||
|
groupBy = append(groupBy, string(resourceLbl))
|
||||||
|
groupBy = append(groupBy, extraGroupBy...)
|
||||||
|
|
||||||
|
args := queryTemplateArgs{
|
||||||
|
Series: series,
|
||||||
|
LabelMatchers: strings.Join(exprs, ","),
|
||||||
|
LabelValuesByName: valuesByName,
|
||||||
|
GroupBy: strings.Join(groupBy, ","),
|
||||||
|
GroupBySlice: groupBy,
|
||||||
|
}
|
||||||
|
queryBuff := new(bytes.Buffer)
|
||||||
|
if err := q.template.Execute(queryBuff, args); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if queryBuff.Len() == 0 {
|
||||||
|
return "", fmt.Errorf("empty query produced by metrics query template")
|
||||||
|
}
|
||||||
|
|
||||||
|
return prom.Selector(queryBuff.String()), nil
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue