mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-07 10:17:51 +00:00
More implementation work in conversion.
Notable next steps include: * Intelligently select the aggregation method (rate/avg_over_time/etc). * Intelligenty select the aggregation window. * Figure out how to handle "namespace", if at all. * Fix the crazy type conversion in `sample_converter.go`.
This commit is contained in:
parent
d90012439c
commit
fe0859aa5c
6 changed files with 99 additions and 30 deletions
|
|
@ -4,12 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
s "strings"
|
s "strings"
|
||||||
|
|
||||||
|
provider "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider/metric-converter"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/selection"
|
"k8s.io/apimachinery/pkg/selection"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExternalMetricQueryBuilder interface {
|
type ExternalMetricQueryBuilder interface {
|
||||||
BuildPrometheusQuery(namespace string, metricName string, metricSelector labels.Selector) string
|
BuildPrometheusQuery(namespace string, metricName string, metricSelector labels.Selector, queryMetadata provider.QueryMetadata) string
|
||||||
}
|
}
|
||||||
|
|
||||||
type externalMetricQueryBuilder struct {
|
type externalMetricQueryBuilder struct {
|
||||||
|
|
@ -19,13 +20,27 @@ func NewExternalMetricQueryBuilder() ExternalMetricQueryBuilder {
|
||||||
return &externalMetricQueryBuilder{}
|
return &externalMetricQueryBuilder{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *externalMetricQueryBuilder) BuildPrometheusQuery(namespace string, metricName string, metricSelector labels.Selector) string {
|
func (p *externalMetricQueryBuilder) BuildPrometheusQuery(namespace string, metricName string, metricSelector labels.Selector, queryMetadata provider.QueryMetadata) string {
|
||||||
namespaceSelector := p.makeLabelFilter("namespace", "=", namespace)
|
//TODO: At least for my Prometheus install, the "namespace" label doesn't seem to be
|
||||||
otherSelectors := p.convertSelectors(metricSelector)
|
//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, ", ")
|
||||||
|
|
||||||
finalTargets := append([]string{namespaceSelector}, otherSelectors...)
|
//TODO: Both the aggregation method and window should probably be configurable.
|
||||||
joinedLabels := s.Join(finalTargets, ", ")
|
//I don't think we can make assumptions about the nature of someone's metrics.
|
||||||
return fmt.Sprintf("%s{%s}", metricName, joinedLabels)
|
//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}[%ss])", aggregation, metricName, joinedLabels, window)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *externalMetricQueryBuilder) makeLabelFilter(labelName string, operator string, targetValue string) string {
|
func (p *externalMetricQueryBuilder) makeLabelFilter(labelName string, operator string, targetValue string) string {
|
||||||
|
|
@ -72,7 +87,9 @@ func (p *externalMetricQueryBuilder) selectOperator(operator selection.Operator,
|
||||||
func (p *externalMetricQueryBuilder) selectRegexOperator(operator selection.Operator) string {
|
func (p *externalMetricQueryBuilder) selectRegexOperator(operator selection.Operator) string {
|
||||||
switch operator {
|
switch operator {
|
||||||
case selection.Equals:
|
case selection.Equals:
|
||||||
|
case selection.In:
|
||||||
return "=~"
|
return "=~"
|
||||||
|
case selection.NotIn:
|
||||||
case selection.NotEquals:
|
case selection.NotEquals:
|
||||||
return "!~"
|
return "!~"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,14 @@ func NewExternalPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *externalPrometheusProvider) GetExternalMetric(namespace string, metricName string, metricSelector labels.Selector) (*external_metrics.ExternalMetricValueList, error) {
|
func (p *externalPrometheusProvider) GetExternalMetric(namespace string, metricName string, metricSelector labels.Selector) (*external_metrics.ExternalMetricValueList, error) {
|
||||||
query := p.queryBuilder.BuildPrometheusQuery(namespace, metricName, metricSelector)
|
//TODO: Get the appropriate time window and aggregation type from somewhere
|
||||||
|
//based on the metric being selected. Does SeriesRegistry have the metric type cached?
|
||||||
|
queryMetadata := conv.QueryMetadata{
|
||||||
|
MetricName: metricName,
|
||||||
|
WindowInSeconds: 120,
|
||||||
|
Aggregation: "rate",
|
||||||
|
}
|
||||||
|
query := p.queryBuilder.BuildPrometheusQuery(namespace, metricName, metricSelector, queryMetadata)
|
||||||
selector := prom.Selector(query)
|
selector := prom.Selector(query)
|
||||||
|
|
||||||
//TODO: I don't yet know what a context is, but apparently I should use a real one.
|
//TODO: I don't yet know what a context is, but apparently I should use a real one.
|
||||||
|
|
@ -78,10 +85,6 @@ func (p *externalPrometheusProvider) GetExternalMetric(namespace string, metricN
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
queryMetadata := conv.QueryMetadata{
|
|
||||||
MetricName: metricName,
|
|
||||||
WindowInSeconds: 0,
|
|
||||||
}
|
|
||||||
return p.metricConverter.Convert(queryMetadata, queryResults)
|
return p.metricConverter.Convert(queryMetadata, queryResults)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,5 +33,5 @@ func (c *matrixConverter) Convert(metadata QueryMetadata, queryResult prom.Query
|
||||||
|
|
||||||
func (c *matrixConverter) convert(result *model.Matrix) (*external_metrics.ExternalMetricValueList, error) {
|
func (c *matrixConverter) convert(result *model.Matrix) (*external_metrics.ExternalMetricValueList, error) {
|
||||||
//TODO: Implementation.
|
//TODO: Implementation.
|
||||||
return nil, nil
|
return nil, errors.New("converting Matrix results is not yet supported")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,6 @@ package provider
|
||||||
type QueryMetadata struct {
|
type QueryMetadata struct {
|
||||||
MetricName string
|
MetricName string
|
||||||
WindowInSeconds int64
|
WindowInSeconds int64
|
||||||
|
//TODO: Type this?
|
||||||
|
Aggregation string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
53
pkg/custom-provider/metric-converter/sample_converter.go
Normal file
53
pkg/custom-provider/metric-converter/sample_converter.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
package provider
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/prometheus/common/model"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/metrics/pkg/apis/external_metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sampleConverter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
//SampleConverter is capable of translating Prometheus Sample objects
|
||||||
|
//into ExternamMetricValue objects.
|
||||||
|
type SampleConverter interface {
|
||||||
|
Convert(metadata QueryMetadata, sample *model.Sample) (*external_metrics.ExternalMetricValue, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewSampleConverter creates a SampleConverter capable of translating Prometheus Sample objects
|
||||||
|
//into ExternamMetricValue objects.
|
||||||
|
func NewSampleConverter() SampleConverter {
|
||||||
|
return &sampleConverter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sampleConverter) Convert(metadata QueryMetadata, sample *model.Sample) (*external_metrics.ExternalMetricValue, error) {
|
||||||
|
labels := c.convertLabels(sample.Metric)
|
||||||
|
|
||||||
|
singleMetric := external_metrics.ExternalMetricValue{
|
||||||
|
MetricName: string(sample.Metric[model.LabelName("__name__")]),
|
||||||
|
Timestamp: metav1.Time{
|
||||||
|
sample.Timestamp.Time(),
|
||||||
|
},
|
||||||
|
WindowSeconds: &metadata.WindowInSeconds,
|
||||||
|
//TODO: I'm not so sure about this type/conversions.
|
||||||
|
//This can't possibly be the right way to convert this.
|
||||||
|
//Also, does K8S only deal win integer metrics?
|
||||||
|
Value: *resource.NewQuantity(int64(float64(sample.Value)), resource.DecimalSI),
|
||||||
|
MetricLabels: labels,
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Actual errors?
|
||||||
|
return &singleMetric, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sampleConverter) convertLabels(inLabels model.Metric) map[string]string {
|
||||||
|
numLabels := len(inLabels)
|
||||||
|
outLabels := make(map[string]string, numLabels)
|
||||||
|
for labelName, labelVal := range inLabels {
|
||||||
|
outLabels[string(labelName)] = string(labelVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
return outLabels
|
||||||
|
}
|
||||||
|
|
@ -5,18 +5,19 @@ import (
|
||||||
|
|
||||||
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||||
"github.com/prometheus/common/model"
|
"github.com/prometheus/common/model"
|
||||||
"k8s.io/apimachinery/pkg/api/resource"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/metrics/pkg/apis/external_metrics"
|
"k8s.io/metrics/pkg/apis/external_metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
type vectorConverter struct {
|
type vectorConverter struct {
|
||||||
|
SampleConverter SampleConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewVectorConverter creates a VectorConverter capable of converting
|
//NewVectorConverter creates a VectorConverter capable of converting
|
||||||
//vector Prometheus query results into external metric types.
|
//vector Prometheus query results into external metric types.
|
||||||
func NewVectorConverter() MetricConverter {
|
func NewVectorConverter(sampleConverter *SampleConverter) MetricConverter {
|
||||||
return &vectorConverter{}
|
return &vectorConverter{
|
||||||
|
SampleConverter: *sampleConverter,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *vectorConverter) Convert(metadata QueryMetadata, queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) {
|
func (c *vectorConverter) Convert(metadata QueryMetadata, queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) {
|
||||||
|
|
@ -45,20 +46,13 @@ func (c *vectorConverter) convert(metadata QueryMetadata, result model.Vector) (
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, val := range result {
|
for _, val := range result {
|
||||||
singleMetric := external_metrics.ExternalMetricValue{
|
//TODO: Care about potential errors here.
|
||||||
MetricName: string(val.Metric[model.LabelName("__name__")]),
|
singleMetric, _ := c.SampleConverter.Convert(metadata, val)
|
||||||
Timestamp: metav1.Time{
|
items = append(items, *singleMetric)
|
||||||
val.Timestamp.Time(),
|
|
||||||
},
|
|
||||||
WindowSeconds: &metadata.WindowInSeconds,
|
|
||||||
//TODO: I'm not so sure about this type/conversions.
|
|
||||||
//This can't possibly be the right way to convert this.
|
|
||||||
//Also, does K8S only deal win integer metrics?
|
|
||||||
Value: *resource.NewQuantity(int64(float64(val.Value)), resource.DecimalSI),
|
|
||||||
}
|
|
||||||
|
|
||||||
items = append(items, singleMetric)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metricValueList = external_metrics.ExternalMetricValueList{
|
||||||
|
Items: items,
|
||||||
|
}
|
||||||
return &metricValueList, nil
|
return &metricValueList, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue