mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 01:38:10 +00:00
* Wiped out a ton of warnings about comments. * Created consts for `MetricType` values. * `externalInfoMap` can now track multiple series with the same name/namespace and different labels. * `namespace` parameter of external metrics queries is now respected (albeit very rudimentarily) * Metric converter values for external metrics are now converted the same way as for custom metrics (probably still some opportunity for consolidation). * Lots of TODOs actually done. * Deleted a lot of commented out code.
285 lines
8.6 KiB
Go
285 lines
8.6 KiB
Go
package provider
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
|
"github.com/directxman12/k8s-prometheus-adapter/pkg/config"
|
|
)
|
|
|
|
var nsGroupResource = schema.GroupResource{Resource: "namespaces"}
|
|
|
|
// MetricNamer knows how to convert Prometheus series names and label names to
|
|
// metrics API resources, and vice-versa. MetricNamers should be safe to access
|
|
// concurrently. Returned group-resources are "normalized" as per the
|
|
// MetricInfo#Normalized method. Group-resources passed as arguments must
|
|
// themselves be normalized.
|
|
type MetricNamer interface {
|
|
// Selector produces the appropriate Prometheus series selector to match all
|
|
// series handlable by this namer.
|
|
Selector() prom.Selector
|
|
// FilterSeries checks to see which of the given series match any additional
|
|
// constrains beyond the series query. It's assumed that the series given
|
|
// already matche the series query.
|
|
// FilterSeries(series []prom.Series) []prom.Series
|
|
SeriesFilterer() SeriesFilterer
|
|
ResourceConverter() ResourceConverter
|
|
|
|
// MetricNameForSeries returns the name (as presented in the API) for a given series.
|
|
// MetricNameForSeries(series prom.Series) (string, error)
|
|
// QueryForSeries returns the query for a given series (not API metric name), with
|
|
// the given namespace name (if relevant), resource, and resource names.
|
|
QueryForSeries(series string, resource schema.GroupResource, namespace string, names ...string) (prom.Selector, error)
|
|
QueryForExternalSeries(namespace string, series string, metricSelector labels.Selector) (prom.Selector, error)
|
|
IdentifySeries(series prom.Series) (seriesIdentity, error)
|
|
MetricType() config.MetricType
|
|
ExternalMetricNamespaceLabelName() string
|
|
}
|
|
|
|
type seriesIdentity struct {
|
|
resources []schema.GroupResource
|
|
namespaced bool
|
|
name string
|
|
}
|
|
|
|
func (n *metricNamer) Selector() prom.Selector {
|
|
return n.seriesQuery
|
|
}
|
|
|
|
type metricNamer struct {
|
|
seriesQuery prom.Selector
|
|
|
|
resourceConverter ResourceConverter
|
|
queryBuilder QueryBuilder
|
|
seriesFilterer SeriesFilterer
|
|
metricNameConverter MetricNameConverter
|
|
mapper apimeta.RESTMapper
|
|
|
|
metricType config.MetricType
|
|
externalMetricNamespaceLabel string
|
|
}
|
|
|
|
// 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) MetricType() config.MetricType {
|
|
return n.metricType
|
|
}
|
|
|
|
func (n *metricNamer) ExternalMetricNamespaceLabelName() string {
|
|
return n.externalMetricNamespaceLabel
|
|
}
|
|
|
|
func (n *metricNamer) IdentifySeries(series prom.Series) (seriesIdentity, error) {
|
|
// TODO: warn if it doesn't match any resources
|
|
resources, namespaced := n.resourceConverter.ResourcesForSeries(series)
|
|
name, err := n.metricNameConverter.GetMetricNameForSeries(series)
|
|
|
|
result := seriesIdentity{
|
|
resources: resources,
|
|
namespaced: namespaced,
|
|
name: name,
|
|
}
|
|
|
|
return result, err
|
|
}
|
|
|
|
func (n *metricNamer) SeriesFilterer() SeriesFilterer {
|
|
return n.seriesFilterer
|
|
}
|
|
|
|
func (n *metricNamer) ResourceConverter() ResourceConverter {
|
|
return n.resourceConverter
|
|
}
|
|
|
|
func (n *metricNamer) createQueryPartsFromSelector(metricSelector labels.Selector) []queryPart {
|
|
requirements, _ := metricSelector.Requirements()
|
|
|
|
selectors := []queryPart{}
|
|
for i := 0; i < len(requirements); i++ {
|
|
selector := n.convertRequirement(requirements[i])
|
|
|
|
selectors = append(selectors, selector)
|
|
}
|
|
|
|
return selectors
|
|
}
|
|
|
|
func (n *metricNamer) convertRequirement(requirement labels.Requirement) queryPart {
|
|
labelName := requirement.Key()
|
|
values := requirement.Values().List()
|
|
|
|
return queryPart{
|
|
labelName: labelName,
|
|
values: values,
|
|
}
|
|
}
|
|
|
|
type queryPart struct {
|
|
labelName string
|
|
values []string
|
|
}
|
|
|
|
func (n *metricNamer) buildNamespaceQueryPartForSeries(namespace string) (queryPart, error) {
|
|
result := queryPart{}
|
|
|
|
//If we've been given a namespace, then we need to set up
|
|
//the label requirements to target that namespace.
|
|
if namespace != "" {
|
|
namespaceLbl, err := n.ResourceConverter().LabelForResource(nsGroupResource)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
|
|
values := []string{namespace}
|
|
|
|
result = queryPart{
|
|
values: values,
|
|
labelName: string(namespaceLbl),
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (n *metricNamer) buildResourceQueryPartForSeries(resource schema.GroupResource, names ...string) (queryPart, error) {
|
|
result := queryPart{}
|
|
|
|
//If we've been given a resource, then we need to set up
|
|
//the label requirements to target that resource.
|
|
resourceLbl, err := n.ResourceConverter().LabelForResource(resource)
|
|
if err != nil {
|
|
return result, err
|
|
}
|
|
|
|
result = queryPart{
|
|
labelName: string(resourceLbl),
|
|
values: names,
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (n *metricNamer) QueryForSeries(series string, resource schema.GroupResource, namespace string, names ...string) (prom.Selector, error) {
|
|
queryParts := []queryPart{}
|
|
|
|
//Build up the namespace part of the query.
|
|
namespaceQueryPart, err := n.buildNamespaceQueryPartForSeries(namespace)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
queryParts = append(queryParts, namespaceQueryPart)
|
|
|
|
//Build up the resource part of the query.
|
|
resourceQueryPart, err := n.buildResourceQueryPartForSeries(resource, names...)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
queryParts = append(queryParts, resourceQueryPart)
|
|
|
|
return n.queryBuilder.BuildSelector(series, resourceQueryPart.labelName, []string{resourceQueryPart.labelName}, queryParts)
|
|
}
|
|
|
|
// NamersFromConfig produces a MetricNamer for each rule in the given config.
|
|
func NamersFromConfig(cfg *config.MetricsDiscoveryConfig, mapper apimeta.RESTMapper) ([]MetricNamer, error) {
|
|
namers := make([]MetricNamer, len(cfg.Rules))
|
|
for i, rule := range cfg.Rules {
|
|
var err error
|
|
|
|
resourceConverter, err := NewResourceConverter(rule.Resources.Template, rule.Resources.Overrides, mapper)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to create ResourceConverter associated with series query %q: %v", rule.SeriesQuery, err)
|
|
}
|
|
|
|
queryBuilder, err := NewQueryBuilder(rule.MetricsQuery)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to create a QueryBuilder associated with series query %q: %v", rule.SeriesQuery, err)
|
|
}
|
|
|
|
seriesFilterer, err := NewSeriesFilterer(rule.SeriesFilters)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to create a SeriesFilter associated with series query %q: %v", rule.SeriesQuery, err)
|
|
}
|
|
|
|
if rule.Name.Matches != "" {
|
|
err := seriesFilterer.AddRequirement(config.RegexFilter{Is: rule.Name.Matches})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to apply the series name filter from name rules associated with series query %q: %v", rule.SeriesQuery, err)
|
|
}
|
|
}
|
|
|
|
metricNameConverter, err := NewMetricNameConverter(rule.Name)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to create a MetricNameConverter associated with series query %q: %v", rule.SeriesQuery, err)
|
|
}
|
|
|
|
namespaceLabel := ""
|
|
if rule.MetricType == config.External {
|
|
namespaceLabel = rule.ExternalMetricNamespaceLabelName
|
|
}
|
|
|
|
metricType := rule.MetricType
|
|
if metricType == config.MetricType("") {
|
|
metricType = config.Custom
|
|
}
|
|
|
|
namer := &metricNamer{
|
|
seriesQuery: prom.Selector(rule.SeriesQuery),
|
|
mapper: mapper,
|
|
|
|
resourceConverter: resourceConverter,
|
|
queryBuilder: queryBuilder,
|
|
seriesFilterer: seriesFilterer,
|
|
metricNameConverter: metricNameConverter,
|
|
metricType: metricType,
|
|
externalMetricNamespaceLabel: namespaceLabel,
|
|
}
|
|
|
|
namers[i] = namer
|
|
}
|
|
|
|
return namers, nil
|
|
}
|
|
|
|
func (n *metricNamer) buildNamespaceQueryPartForExternalSeries(namespace string) (queryPart, error) {
|
|
return queryPart{
|
|
labelName: n.externalMetricNamespaceLabel,
|
|
values: []string{namespace},
|
|
}, nil
|
|
}
|
|
|
|
func (n *metricNamer) QueryForExternalSeries(namespace string, series string, metricSelector labels.Selector) (prom.Selector, error) {
|
|
queryParts := []queryPart{}
|
|
|
|
if namespace != "" {
|
|
//Build up the namespace part of the query.
|
|
namespaceQueryPart, err := n.buildNamespaceQueryPartForExternalSeries(namespace)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
queryParts = append(queryParts, namespaceQueryPart)
|
|
}
|
|
|
|
//Build up the query parts from the selector.
|
|
queryParts = append(queryParts, n.createQueryPartsFromSelector(metricSelector)...)
|
|
|
|
selector, err := n.queryBuilder.BuildSelector(series, "", []string{}, queryParts)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return selector, nil
|
|
}
|