Breaking down some more components, adding tests.

* Some bug fixes found during testing/test repair.
* Trying to tease apart the various responsibilities of `metricNamer` into smaller chunks and adding tests for each individual chunk.
* Updating the `provider_test` and `series_registry_test` to fix failures.
This commit is contained in:
Tony Compton 2018-07-19 10:53:48 -04:00
parent 76217a552b
commit fc88e6e57a
18 changed files with 1038 additions and 516 deletions

View file

@ -0,0 +1,96 @@
package provider
import (
"bytes"
"fmt"
"strings"
"text/template"
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
)
type QueryBuilder interface {
BuildSelector(seriesName string, groupBy string, groupBySlice []string, queryParts []queryPart) (prom.Selector, error)
}
type queryBuilder struct {
metricsQueryTemplate *template.Template
}
func NewQueryBuilder(metricsQuery string) (QueryBuilder, error) {
metricsQueryTemplate, err := template.New("metrics-query").Delims("<<", ">>").Parse(metricsQuery)
if err != nil {
return nil, fmt.Errorf("unable to parse metrics query template %q: %v", metricsQuery, err)
}
return &queryBuilder{
metricsQueryTemplate: metricsQueryTemplate,
}, nil
}
func (n *queryBuilder) BuildSelector(seriesName string, groupBy string, groupBySlice []string, queryParts []queryPart) (prom.Selector, error) {
//Convert our query parts into the types we need for our template.
exprs, valuesByName := n.processQueryParts(queryParts)
args := queryTemplateArgs{
Series: seriesName,
LabelMatchers: strings.Join(exprs, ","),
LabelValuesByName: valuesByName,
GroupBy: groupBy,
GroupBySlice: groupBySlice,
}
selector, err := n.createSelectorFromTemplateArgs(args)
if err != nil {
return "", err
}
return selector, nil
}
func (n *queryBuilder) createSelectorFromTemplateArgs(args queryTemplateArgs) (prom.Selector, error) {
//Turn our template arguments into a Selector.
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 *queryBuilder) processQueryParts(queryParts []queryPart) ([]string, map[string][]string) {
//Contains the expressions that we want to include as part of the query to Prometheus.
//e.g. "namespace=my-namespace"
//e.g. "some_label=some-value"
var exprs []string
//Contains the list of label values we're targeting, by namespace.
//e.g. "some_label" => ["value-one", "value-two"]
valuesByName := map[string][]string{}
//Convert our query parts into template arguments.
for _, qPart := range queryParts {
//Be resilient against bad inputs.
//We obviously can't generate label filters for these cases.
if qPart.labelName == "" || len(qPart.values) == 0 {
continue
}
targetValue := qPart.values[0]
matcher := prom.LabelEq
if len(qPart.values) > 1 {
targetValue = strings.Join(qPart.values, "|")
matcher = prom.LabelMatches
}
expression := matcher(qPart.labelName, targetValue)
exprs = append(exprs, expression)
valuesByName[qPart.labelName] = qPart.values
}
return exprs, valuesByName
}