prometheus-adapter/pkg/custom-provider/external_metric_query_builder.go
Tony Compton 277734dcdb Fixing some silly errors.
Always verify your builds before you push, kids. 👎
2018-06-29 12:11:30 -04:00

111 lines
3.7 KiB
Go

package provider
import (
"fmt"
s "strings"
provider "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider/metric-converter"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
)
type ExternalMetricQueryBuilder interface {
BuildPrometheusQuery(namespace string, metricName string, metricSelector labels.Selector, queryMetadata provider.QueryMetadata) string
}
type externalMetricQueryBuilder struct {
}
func NewExternalMetricQueryBuilder() ExternalMetricQueryBuilder {
return &externalMetricQueryBuilder{}
}
func (p *externalMetricQueryBuilder) BuildPrometheusQuery(namespace string, metricName string, metricSelector labels.Selector, queryMetadata provider.QueryMetadata) string {
//TODO: At least for my Prometheus install, the "namespace" label doesn't seem to be
//directly applied to the time series. I'm using prometheus-operator. The grafana dashboards
//seem to query for the pods in a namespace from kube_pod_info and then apply pod-specific
//label filters. This might need some more thought. Disabling for now.
// namespaceSelector := p.makeLabelFilter("namespace", "=", namespace)
labelSelectors := p.convertSelectors(metricSelector)
joinedLabels := s.Join(labelSelectors, ", ")
//TODO: Both the aggregation method and window should probably be configurable.
//I don't think we can make assumptions about the nature of someone's metrics.
//I'm guessing this might be covered by the recently added advanced configuration
//code, but I haven't yet had an opportunity to dig into that and understand it.
//We'll leave this here for testing purposes for now.
//As reasonable defaults, maybe:
//rate(...) for counters
//avg_over_time(...) for gauges
//I'm guessing that SeriesRegistry might store the metric type, but I haven't looked yet.
aggregation := queryMetadata.Aggregation
window := queryMetadata.WindowInSeconds
return fmt.Sprintf("%s(%s{%s}[%ds])", aggregation, metricName, joinedLabels, window)
}
func (p *externalMetricQueryBuilder) makeLabelFilter(labelName string, operator string, targetValue string) string {
return fmt.Sprintf("%s%s\"%s\"", labelName, operator, targetValue)
}
func (p *externalMetricQueryBuilder) convertSelectors(metricSelector labels.Selector) []string {
requirements, _ := metricSelector.Requirements()
selectors := []string{}
for i := 0; i < len(requirements); i++ {
selector := p.convertRequirement(requirements[i])
selectors = append(selectors, selector)
}
return selectors
}
func (p *externalMetricQueryBuilder) convertRequirement(requirement labels.Requirement) string {
labelName := requirement.Key()
values := requirement.Values().List()
stringValues := values[0]
valueCount := len(values)
if valueCount > 1 {
stringValues = s.Join(values, "|")
}
operator := p.selectOperator(requirement.Operator(), valueCount)
return p.makeLabelFilter(labelName, operator, stringValues)
}
func (p *externalMetricQueryBuilder) selectOperator(operator selection.Operator, valueCount int) string {
if valueCount > 1 {
return p.selectRegexOperator(operator)
}
return p.selectSingleValueOperator(operator)
}
func (p *externalMetricQueryBuilder) selectRegexOperator(operator selection.Operator) string {
switch operator {
case selection.Equals:
case selection.In:
return "=~"
case selection.NotIn:
case selection.NotEquals:
return "!~"
}
//TODO: Cover more cases, supply appropriate errors for any unhandled cases.
return "="
}
func (p *externalMetricQueryBuilder) selectSingleValueOperator(operator selection.Operator) string {
switch operator {
case selection.Equals:
return "="
case selection.NotEquals:
return "!="
}
//TODO: Cover more cases, supply appropriate errors for any unhandled cases.
return "="
}