mirror of
https://github.com/kubernetes-sigs/prometheus-adapter.git
synced 2026-04-06 09:47:54 +00:00
Cleaning up.
* 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.
This commit is contained in:
parent
9641e70005
commit
a94494337e
18 changed files with 155 additions and 188 deletions
|
|
@ -34,7 +34,10 @@ type DiscoveryRule struct {
|
|||
MetricsQuery string `yaml:"metricsQuery,omitempty"`
|
||||
// MetricType identifies whether the metrics derived from this rule should be classified
|
||||
// as external or custom metrics.
|
||||
MetricType MetricType `yaml:"metricType"`
|
||||
MetricType MetricType `yaml:"metricType,omitempty"`
|
||||
// ExternalMetricNamespaceLabelName identifies what Prometheus label should be examined
|
||||
// to apply a namespace to metrics creates from this rule.
|
||||
ExternalMetricNamespaceLabelName string `yaml:"externalMetricNamespaceLabelName,omitempty"`
|
||||
}
|
||||
|
||||
// RegexFilter is a filter that matches positively or negatively against a regex.
|
||||
|
|
@ -79,3 +82,12 @@ type NameMapping struct {
|
|||
|
||||
//MetricType identifies whether a given metric should be handled and interpreted as a Custom or External metric.
|
||||
type MetricType string
|
||||
|
||||
// Operator represents a key/field's relationship to value(s).
|
||||
// See labels.Requirement and fields.Requirement for more details.
|
||||
type Operator string
|
||||
|
||||
const (
|
||||
External MetricType = "External"
|
||||
Custom MetricType = "Custom"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -48,13 +48,15 @@ type MetricLister interface {
|
|||
ListAllMetrics() (metricUpdateResult, error)
|
||||
}
|
||||
|
||||
//A MetricListerWithNotification is a MetricLister that has the ability to notify listeners
|
||||
//when new metric data is available.
|
||||
type MetricListerWithNotification interface {
|
||||
//It can list metrics, just like a normal MetricLister.
|
||||
MetricLister
|
||||
//Because it periodically pulls metrics, it needs to be Runnable.
|
||||
Runnable
|
||||
//It provides notifications when it has new data to supply.
|
||||
|
||||
//AddNotificationReceiver registers a callback to be invoked when new metric data is available.
|
||||
AddNotificationReceiver(func(metricUpdateResult))
|
||||
//UpdateNow forces an immediate refresh from the source data. Primarily for test purposes.
|
||||
UpdateNow()
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +66,7 @@ type basicMetricLister struct {
|
|||
lookback time.Duration
|
||||
}
|
||||
|
||||
//NewBasicMetricLister creates a MetricLister that is capable of interactly directly with Prometheus to list metrics.
|
||||
func NewBasicMetricLister(promClient prom.Client, namers []MetricNamer, lookback time.Duration) MetricLister {
|
||||
lister := basicMetricLister{
|
||||
promClient: promClient,
|
||||
|
|
|
|||
|
|
@ -5,24 +5,37 @@ import (
|
|||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
//ExportedMetric is a description of an available metric.
|
||||
type ExportedMetric struct {
|
||||
MetricName string
|
||||
Labels labels.Set
|
||||
Namespace string
|
||||
}
|
||||
|
||||
//ExternalInfoMap is a data object that accepts and organizes information
|
||||
//about available metrics.
|
||||
type ExternalInfoMap interface {
|
||||
//Begins tracking a metric, returning it to the caller.
|
||||
TrackMetric(metricName string, generatedBy MetricNamer) ExternalMetricData
|
||||
//Exports a collection of all of the metrics currently being tracked.
|
||||
ExportMetrics() []ExportedMetric
|
||||
//Finds a tracked metric with the given metric name, if it exists.
|
||||
FindMetric(metricName string) (data ExternalMetricData, found bool)
|
||||
}
|
||||
|
||||
//ExternalMetricData is a data object that accepts and organizes information
|
||||
//about the various series/namespaces that a metric is associated with.
|
||||
type ExternalMetricData interface {
|
||||
//MetricName returns the name of the metric represented by this object.
|
||||
MetricName() string
|
||||
//WithSeries associates the provided labels with this metric.
|
||||
WithSeries(labels labels.Set)
|
||||
//WithNamespacedSeries associates the provided labels with this metric, but within a particular namespace.
|
||||
WithNamespacedSeries(namespace string, labels labels.Set)
|
||||
//Exports a collection of all the metrics currently being tracked.
|
||||
ExportMetrics() []ExportedMetric
|
||||
GenerateQuery(selector labels.Selector) (prom.Selector, error)
|
||||
//Generates a query to select the series/values for the metric this object represents.
|
||||
GenerateQuery(namespace string, selector labels.Selector) (prom.Selector, error)
|
||||
}
|
||||
|
||||
type externalInfoMap struct {
|
||||
|
|
@ -31,18 +44,20 @@ type externalInfoMap struct {
|
|||
|
||||
type externalMetricData struct {
|
||||
metricName string
|
||||
namespacedData map[string]labels.Set
|
||||
namespacedData map[string][]labels.Set
|
||||
generatedBy MetricNamer
|
||||
}
|
||||
|
||||
//NewExternalMetricData creates an ExternalMetricData for the provided metric name and namer.
|
||||
func NewExternalMetricData(metricName string, generatedBy MetricNamer) ExternalMetricData {
|
||||
return &externalMetricData{
|
||||
metricName: metricName,
|
||||
generatedBy: generatedBy,
|
||||
namespacedData: map[string]labels.Set{},
|
||||
namespacedData: map[string][]labels.Set{},
|
||||
}
|
||||
}
|
||||
|
||||
//NewExternalInfoMap creates an empty ExternalInfoMap for storing external metric information.
|
||||
func NewExternalInfoMap() ExternalInfoMap {
|
||||
return &externalInfoMap{
|
||||
metrics: map[string]ExternalMetricData{},
|
||||
|
|
@ -78,18 +93,20 @@ func (d *externalMetricData) MetricName() string {
|
|||
return d.metricName
|
||||
}
|
||||
|
||||
func (d *externalMetricData) GenerateQuery(selector labels.Selector) (prom.Selector, error) {
|
||||
return d.generatedBy.QueryForExternalSeries(d.metricName, selector)
|
||||
func (d *externalMetricData) GenerateQuery(namespace string, selector labels.Selector) (prom.Selector, error) {
|
||||
return d.generatedBy.QueryForExternalSeries(namespace, d.metricName, selector)
|
||||
}
|
||||
|
||||
func (d *externalMetricData) ExportMetrics() []ExportedMetric {
|
||||
results := make([]ExportedMetric, 0)
|
||||
for namespace, labels := range d.namespacedData {
|
||||
results = append(results, ExportedMetric{
|
||||
Labels: labels,
|
||||
MetricName: d.metricName,
|
||||
Namespace: namespace,
|
||||
})
|
||||
for namespace, labelSets := range d.namespacedData {
|
||||
for _, labelSet := range labelSets {
|
||||
results = append(results, ExportedMetric{
|
||||
Labels: labelSet,
|
||||
MetricName: d.metricName,
|
||||
Namespace: namespace,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
|
|
@ -99,10 +116,13 @@ func (d *externalMetricData) WithSeries(labels labels.Set) {
|
|||
d.WithNamespacedSeries("", labels)
|
||||
}
|
||||
|
||||
func (d *externalMetricData) WithNamespacedSeries(namespace string, labels labels.Set) {
|
||||
func (d *externalMetricData) WithNamespacedSeries(namespace string, seriesLabels labels.Set) {
|
||||
data, found := d.namespacedData[namespace]
|
||||
if !found {
|
||||
data = labels
|
||||
d.namespacedData[namespace] = data
|
||||
data = []labels.Set{}
|
||||
}
|
||||
|
||||
data = append(data, seriesLabels)
|
||||
d.namespacedData[namespace] = data
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,16 +15,7 @@ import (
|
|||
conv "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider/metric-converter"
|
||||
)
|
||||
|
||||
//TODO: Make sure everything has the proper licensing disclosure at the top.
|
||||
//TODO: I'd like to move these files into another directory, but the compiler was giving me
|
||||
//some static around unexported types. I'm going to leave things as-is for now, but it
|
||||
//might be worthwhile to, once the shared components are discovered, move some things around.
|
||||
|
||||
//TODO: Some of these members may not be necessary.
|
||||
//Some of them are definitely duplicated between the
|
||||
//external and custom providers. They should probably share
|
||||
//the same instances of these objects (especially the SeriesRegistry)
|
||||
//to cut down on unnecessary chatter/bookkeeping.
|
||||
//TODO: AC - Make sure everything has the proper licensing disclosure at the top.
|
||||
type externalPrometheusProvider struct {
|
||||
promClient prom.Client
|
||||
metricConverter conv.MetricConverter
|
||||
|
|
@ -32,13 +23,7 @@ type externalPrometheusProvider struct {
|
|||
seriesRegistry ExternalSeriesRegistry
|
||||
}
|
||||
|
||||
//TODO: It probably makes more sense to, once this is functional and complete, roll the
|
||||
//prometheusProvider and externalPrometheusProvider up into a single type
|
||||
//that implements both interfaces or provide a thin wrapper that composes them.
|
||||
//Just glancing at start.go looks like it would be much more straightforward
|
||||
//to do one of those two things instead of trying to run the two providers
|
||||
//independently.
|
||||
|
||||
//NewExternalPrometheusProvider creates an ExternalMetricsProvider capable of responding to Kubernetes requests for external metric data.
|
||||
func NewExternalPrometheusProvider(seriesRegistry ExternalSeriesRegistry, promClient prom.Client, converter conv.MetricConverter) provider.ExternalMetricsProvider {
|
||||
return &externalPrometheusProvider{
|
||||
promClient: promClient,
|
||||
|
|
@ -48,21 +33,17 @@ func NewExternalPrometheusProvider(seriesRegistry ExternalSeriesRegistry, promCl
|
|||
}
|
||||
|
||||
func (p *externalPrometheusProvider) GetExternalMetric(namespace string, metricName string, metricSelector labels.Selector) (*external_metrics.ExternalMetricValueList, error) {
|
||||
selector, found := p.seriesRegistry.QueryForMetric(metricName, metricSelector)
|
||||
selector, found := p.seriesRegistry.QueryForMetric(namespace, metricName, metricSelector)
|
||||
|
||||
if !found {
|
||||
return &external_metrics.ExternalMetricValueList{
|
||||
Items: []external_metrics.ExternalMetricValue{},
|
||||
}, nil
|
||||
}
|
||||
// query := p.queryBuilder.BuildPrometheusQuery(namespace, metricName, metricSelector, queryMetadata)
|
||||
|
||||
//TODO: I don't yet know what a context is, but apparently I should use a real one.
|
||||
queryResults, err := p.promClient.Query(context.TODO(), pmodel.Now(), selector)
|
||||
|
||||
if err != nil {
|
||||
//TODO: Is this how folks normally deal w/ errors? Just propagate them upwards?
|
||||
//I should go look at what the customProvider does.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,31 +14,33 @@ import (
|
|||
"github.com/directxman12/k8s-prometheus-adapter/pkg/config"
|
||||
)
|
||||
|
||||
//ExternalSeriesRegistry acts as the top-level converter for transforming Kubernetes requests
|
||||
//for external metrics into Prometheus queries.
|
||||
type ExternalSeriesRegistry interface {
|
||||
// ListAllMetrics lists all metrics known to this registry
|
||||
ListAllMetrics() []provider.ExternalMetricInfo
|
||||
QueryForMetric(metricName string, metricSelector labels.Selector) (query prom.Selector, found bool)
|
||||
QueryForMetric(namespace string, metricName string, metricSelector labels.Selector) (query prom.Selector, found bool)
|
||||
}
|
||||
|
||||
// overridableSeriesRegistry is a basic SeriesRegistry
|
||||
type externalSeriesRegistry struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
externalInfo map[string]seriesInfo
|
||||
// metrics is the list of all known metrics
|
||||
metrics []provider.ExternalMetricInfo
|
||||
|
||||
mapper apimeta.RESTMapper
|
||||
|
||||
metricLister MetricListerWithNotification
|
||||
tonyExternalInfo ExternalInfoMap
|
||||
metricLister MetricListerWithNotification
|
||||
externalMetricInfo ExternalInfoMap
|
||||
}
|
||||
|
||||
//NewExternalSeriesRegistry creates an ExternalSeriesRegistry driven by the data from the provided MetricLister.
|
||||
func NewExternalSeriesRegistry(lister MetricListerWithNotification, mapper apimeta.RESTMapper) ExternalSeriesRegistry {
|
||||
var registry = externalSeriesRegistry{
|
||||
mapper: mapper,
|
||||
metricLister: lister,
|
||||
tonyExternalInfo: NewExternalInfoMap(),
|
||||
mapper: mapper,
|
||||
metricLister: lister,
|
||||
externalMetricInfo: NewExternalInfoMap(),
|
||||
}
|
||||
|
||||
lister.AddNotificationReceiver(registry.onNewDataAvailable)
|
||||
|
|
@ -50,7 +52,7 @@ func (r *externalSeriesRegistry) filterMetrics(result metricUpdateResult) metric
|
|||
namers := make([]MetricNamer, 0)
|
||||
series := make([][]prom.Series, 0)
|
||||
|
||||
targetType := config.MetricType("External")
|
||||
targetType := config.External
|
||||
|
||||
for i, namer := range result.namers {
|
||||
if namer.MetricType() == targetType {
|
||||
|
|
@ -98,10 +100,16 @@ func (r *externalSeriesRegistry) onNewDataAvailable(result metricUpdateResult) {
|
|||
// namespaced := identity.namespaced
|
||||
name := identity.name
|
||||
labels := r.convertLabels(series.Labels)
|
||||
//TODO: Figure out the namespace, if applicable
|
||||
metricNs := ""
|
||||
|
||||
//Check for a label indicating namespace.
|
||||
metricNs, found := series.Labels[model.LabelName(namer.ExternalMetricNamespaceLabelName())]
|
||||
|
||||
if !found {
|
||||
metricNs = ""
|
||||
}
|
||||
|
||||
trackedMetric := updatedCache.TrackMetric(name, namer)
|
||||
trackedMetric.WithNamespacedSeries(metricNs, labels)
|
||||
trackedMetric.WithNamespacedSeries(string(metricNs), labels)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +120,7 @@ func (r *externalSeriesRegistry) onNewDataAvailable(result metricUpdateResult) {
|
|||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
r.tonyExternalInfo = updatedCache
|
||||
r.externalMetricInfo = updatedCache
|
||||
r.metrics = convertedMetrics
|
||||
}
|
||||
|
||||
|
|
@ -135,29 +143,20 @@ func (r *externalSeriesRegistry) ListAllMetrics() []provider.ExternalMetricInfo
|
|||
return r.metrics
|
||||
}
|
||||
|
||||
func (r *externalSeriesRegistry) QueryForMetric(metricName string, metricSelector labels.Selector) (query prom.Selector, found bool) {
|
||||
func (r *externalSeriesRegistry) QueryForMetric(namespace string, metricName string, metricSelector labels.Selector) (query prom.Selector, found bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
metric, found := r.tonyExternalInfo.FindMetric(metricName)
|
||||
metric, found := r.externalMetricInfo.FindMetric(metricName)
|
||||
|
||||
if !found {
|
||||
glog.V(10).Infof("external metric %q not registered", metricName)
|
||||
return "", false
|
||||
}
|
||||
|
||||
query, err := metric.GenerateQuery(metricSelector)
|
||||
// info, infoFound := r.info[metricInfo]
|
||||
// if !infoFound {
|
||||
// //TODO: Weird that it switches between types here.
|
||||
// glog.V(10).Infof("metric %v not registered", metricInfo)
|
||||
// return "", false
|
||||
// }
|
||||
query, err := metric.GenerateQuery(namespace, metricSelector)
|
||||
|
||||
// query, err := info.namer.QueryForExternalSeries(info.seriesName, metricSelector)
|
||||
if err != nil {
|
||||
//TODO: See what was being .String() and implement that for ExternalMetricInfo.
|
||||
// errorVal := metricInfo.String()
|
||||
errorVal := "something"
|
||||
glog.Errorf("unable to construct query for metric %s: %v", errorVal, err)
|
||||
glog.Errorf("unable to construct query for external metric %s: %v", metricName, err)
|
||||
return "", false
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,6 @@ func (c *matrixConverter) Convert(queryResult prom.QueryResult) (*external_metri
|
|||
}
|
||||
|
||||
func (c *matrixConverter) convert(result *model.Matrix) (*external_metrics.ExternalMetricValueList, error) {
|
||||
//TODO: Implementation.
|
||||
//TODO: AC - Implementation.
|
||||
return nil, errors.New("converting Matrix results is not yet supported")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,14 +30,10 @@ func (c *sampleConverter) Convert(sample *model.Sample) (*external_metrics.Exter
|
|||
Timestamp: metav1.Time{
|
||||
sample.Timestamp.Time(),
|
||||
},
|
||||
//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),
|
||||
Value: *resource.NewMilliQuantity(int64(sample.Value*1000.0), resource.DecimalSI),
|
||||
MetricLabels: labels,
|
||||
}
|
||||
|
||||
//TODO: Actual errors?
|
||||
return &singleMetric, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,18 +35,11 @@ func (c *scalarConverter) Convert(queryResult prom.QueryResult) (*external_metri
|
|||
|
||||
func (c *scalarConverter) convert(input *model.Scalar) (*external_metrics.ExternalMetricValueList, error) {
|
||||
result := external_metrics.ExternalMetricValueList{
|
||||
//Using prometheusProvider.metricsFor(...) as an example,
|
||||
//it seems that I don't need to provide values for
|
||||
//TypeMeta and ListMeta.
|
||||
//TODO: Get some confirmation on this.
|
||||
Items: []external_metrics.ExternalMetricValue{
|
||||
{
|
||||
Timestamp: metav1.Time{
|
||||
input.Timestamp.Time(),
|
||||
},
|
||||
//TODO: I'm not so sure about this type/conversions.
|
||||
//Is there a meaningful loss of precision here?
|
||||
//Does K8S only deal win integer metrics?
|
||||
Value: *resource.NewMilliQuantity(int64(input.Value*1000.0), resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package provider
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||
"github.com/prometheus/common/model"
|
||||
|
|
@ -46,8 +47,13 @@ func (c *vectorConverter) convert(result model.Vector) (*external_metrics.Extern
|
|||
}
|
||||
|
||||
for _, val := range result {
|
||||
//TODO: Care about potential errors here.
|
||||
singleMetric, _ := c.SampleConverter.Convert(val)
|
||||
|
||||
singleMetric, err := c.SampleConverter.Convert(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to convert vector: %v", err)
|
||||
}
|
||||
|
||||
items = append(items, *singleMetric)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/directxman12/k8s-prometheus-adapter/pkg/config"
|
||||
)
|
||||
|
||||
//MetricNameConverter provides functions for naming custom metrics from Promethes series.
|
||||
type MetricNameConverter interface {
|
||||
GetMetricNameForSeries(series prom.Series) (string, error)
|
||||
}
|
||||
|
|
@ -17,6 +18,8 @@ type metricNameConverter struct {
|
|||
nameAs string
|
||||
}
|
||||
|
||||
//NewMetricNameConverter creates a MetricNameConverter capable of translating Prometheus series names
|
||||
//into custom metric names.
|
||||
func NewMetricNameConverter(mapping config.NameMapping) (MetricNameConverter, error) {
|
||||
var nameMatches *regexp.Regexp
|
||||
var err error
|
||||
|
|
|
|||
|
|
@ -34,9 +34,10 @@ type MetricNamer interface {
|
|||
// 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(series string, metricSelector labels.Selector) (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 {
|
||||
|
|
@ -58,7 +59,8 @@ type metricNamer struct {
|
|||
metricNameConverter MetricNameConverter
|
||||
mapper apimeta.RESTMapper
|
||||
|
||||
metricType config.MetricType
|
||||
metricType config.MetricType
|
||||
externalMetricNamespaceLabel string
|
||||
}
|
||||
|
||||
// queryTemplateArgs are the arguments for the metrics query template.
|
||||
|
|
@ -74,6 +76,10 @@ 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)
|
||||
|
|
@ -219,15 +225,26 @@ func NamersFromConfig(cfg *config.MetricsDiscoveryConfig, mapper apimeta.RESTMap
|
|||
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: rule.MetricType,
|
||||
resourceConverter: resourceConverter,
|
||||
queryBuilder: queryBuilder,
|
||||
seriesFilterer: seriesFilterer,
|
||||
metricNameConverter: metricNameConverter,
|
||||
metricType: metricType,
|
||||
externalMetricNamespaceLabel: namespaceLabel,
|
||||
}
|
||||
|
||||
namers[i] = namer
|
||||
|
|
@ -236,8 +253,28 @@ func NamersFromConfig(cfg *config.MetricsDiscoveryConfig, mapper apimeta.RESTMap
|
|||
return namers, nil
|
||||
}
|
||||
|
||||
func (n *metricNamer) QueryForExternalSeries(series string, metricSelector labels.Selector) (prom.Selector, error) {
|
||||
queryParts := n.createQueryPartsFromSelector(metricSelector)
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -87,69 +87,3 @@ func (l *periodicMetricLister) notifyListeners() {
|
|||
func (l *periodicMetricLister) UpdateNow() {
|
||||
l.updateMetrics()
|
||||
}
|
||||
|
||||
// func (l *periodicMetricLister) updateMetrics() (metricUpdateResult, error) {
|
||||
|
||||
// result := metricUpdateResult{
|
||||
// series: make([][]prom.Series, 0),
|
||||
// namers: make([]MetricNamer, 0),
|
||||
// }
|
||||
|
||||
// startTime := pmodel.Now().Add(-1 * l.updateInterval)
|
||||
|
||||
// // these can take a while on large clusters, so launch in parallel
|
||||
// // and don't duplicate
|
||||
// selectors := make(map[prom.Selector]struct{})
|
||||
// selectorSeriesChan := make(chan selectorSeries, len(l.namers))
|
||||
// errs := make(chan error, len(l.namers))
|
||||
// for _, namer := range l.namers {
|
||||
// sel := namer.Selector()
|
||||
// if _, ok := selectors[sel]; ok {
|
||||
// errs <- nil
|
||||
// selectorSeriesChan <- selectorSeries{}
|
||||
// continue
|
||||
// }
|
||||
// selectors[sel] = struct{}{}
|
||||
// go func() {
|
||||
// series, err := l.promClient.Series(context.TODO(), pmodel.Interval{startTime, 0}, sel)
|
||||
// if err != nil {
|
||||
// errs <- fmt.Errorf("unable to fetch metrics for query %q: %v", sel, err)
|
||||
// return
|
||||
// }
|
||||
// errs <- nil
|
||||
// selectorSeriesChan <- selectorSeries{
|
||||
// selector: sel,
|
||||
// series: series,
|
||||
// }
|
||||
// }()
|
||||
// }
|
||||
|
||||
// // don't do duplicate queries when it's just the matchers that change
|
||||
// seriesCacheByQuery := make(map[prom.Selector][]prom.Series)
|
||||
|
||||
// // iterate through, blocking until we've got all results
|
||||
// for range l.namers {
|
||||
// if err := <-errs; err != nil {
|
||||
// return result, fmt.Errorf("unable to update list of all metrics: %v", err)
|
||||
// }
|
||||
// if ss := <-selectorSeriesChan; ss.series != nil {
|
||||
// seriesCacheByQuery[ss.selector] = ss.series
|
||||
// }
|
||||
// }
|
||||
// close(errs)
|
||||
|
||||
// newSeries := make([][]prom.Series, len(l.namers))
|
||||
// for i, namer := range l.namers {
|
||||
// series, cached := seriesCacheByQuery[namer.Selector()]
|
||||
// if !cached {
|
||||
// return result, fmt.Errorf("unable to update list of all metrics: no metrics retrieved for query %q", namer.Selector())
|
||||
// }
|
||||
// newSeries[i] = namer.FilterSeries(series)
|
||||
// }
|
||||
|
||||
// glog.V(10).Infof("Set available metric list from Prometheus to: %v", newSeries)
|
||||
|
||||
// result.series = newSeries
|
||||
// result.namers = l.namers
|
||||
// return result, nil
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -46,12 +46,11 @@ type prometheusProvider struct {
|
|||
SeriesRegistry
|
||||
}
|
||||
|
||||
//NewPrometheusProvider creates an CustomMetricsProvider capable of responding to Kubernetes requests for custom metric data.
|
||||
func NewPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.Interface, promClient prom.Client, namers []MetricNamer, updateInterval time.Duration) (provider.CustomMetricsProvider, Runnable) {
|
||||
//TODO: AC - Consider injecting these objects and calling .Run() before calling this function.
|
||||
basicLister := NewBasicMetricLister(promClient, namers, updateInterval)
|
||||
//TODO: Be sure to run this runnable.
|
||||
// periodicLister, periodicRunnable := NewPeriodicMetricLister(basicLister, updateInterval)
|
||||
periodicLister, _ := NewPeriodicMetricLister(basicLister, updateInterval)
|
||||
|
||||
seriesRegistry := NewBasicSeriesRegistry(periodicLister, mapper)
|
||||
|
||||
return &prometheusProvider{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||
)
|
||||
|
||||
//QueryBuilder provides functions for generating Prometheus queries.
|
||||
type QueryBuilder interface {
|
||||
BuildSelector(seriesName string, groupBy string, groupBySlice []string, queryParts []queryPart) (prom.Selector, error)
|
||||
}
|
||||
|
|
@ -17,6 +18,7 @@ type queryBuilder struct {
|
|||
metricsQueryTemplate *template.Template
|
||||
}
|
||||
|
||||
//NewQueryBuilder creates a QueryBuilder.
|
||||
func NewQueryBuilder(metricsQuery string) (QueryBuilder, error) {
|
||||
metricsQueryTemplate, err := template.New("metrics-query").Delims("<<", ">>").Parse(metricsQuery)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -79,4 +79,4 @@ func TestQueryWithGroupBy(t *testing.T) {
|
|||
require.Equal(t, selector, expectation)
|
||||
}
|
||||
|
||||
//TODO: Ensure that the LabelValuesByName and GroupBySlice placeholders function correctly.
|
||||
//TODO: AC - Ensure that the LabelValuesByName and GroupBySlice placeholders function correctly.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ import (
|
|||
pmodel "github.com/prometheus/common/model"
|
||||
)
|
||||
|
||||
//ResourceConverter is a type for extracting associated Kubernetes GroupResource objects from
|
||||
//Prometheus series and generating appropriate labels to target specific Kubernetes GroupResource
|
||||
//objects.
|
||||
type ResourceConverter interface {
|
||||
// ResourcesForSeries returns the group-resources associated with the given series,
|
||||
// as well as whether or not the given series has the "namespace" resource).
|
||||
|
|
@ -34,6 +37,7 @@ type resourceConverter struct {
|
|||
labelTemplate *template.Template
|
||||
}
|
||||
|
||||
//NewResourceConverter creates a ResourceConverter that will use the provided parameters to map data between Prometheus and Kubernetes.
|
||||
func NewResourceConverter(resourceTemplate string, overrides map[string]config.GroupResource, mapper apimeta.RESTMapper) (ResourceConverter, error) {
|
||||
converter := &resourceConverter{
|
||||
labelToResource: make(map[pmodel.LabelName]schema.GroupResource),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import (
|
|||
"github.com/directxman12/k8s-prometheus-adapter/pkg/config"
|
||||
)
|
||||
|
||||
//SeriesFilterer provides functions for filtering collections of Prometheus series
|
||||
//to only those that meet certain requirements.
|
||||
type SeriesFilterer interface {
|
||||
FilterSeries(series []prom.Series) []prom.Series
|
||||
AddRequirement(filter config.RegexFilter) error
|
||||
|
|
@ -16,6 +18,8 @@ type seriesFilterer struct {
|
|||
seriesMatchers []*reMatcher
|
||||
}
|
||||
|
||||
//NewSeriesFilterer creates a SeriesFilterer that will remove any series that do not
|
||||
//meet the requirements of the provided RegexFilter(s).
|
||||
func NewSeriesFilterer(filters []config.RegexFilter) (SeriesFilterer, error) {
|
||||
seriesMatchers := make([]*reMatcher, len(filters))
|
||||
for i, filterRaw := range filters {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
|
||||
"github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider"
|
||||
apimeta "k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client"
|
||||
"github.com/directxman12/k8s-prometheus-adapter/pkg/config"
|
||||
|
|
@ -49,8 +48,6 @@ type SeriesRegistry interface {
|
|||
// SeriesForMetric looks up the minimum required series information to make a query for the given metric
|
||||
// against the given resource (namespace may be empty for non-namespaced resources)
|
||||
QueryForMetric(info provider.CustomMetricInfo, namespace string, resourceNames ...string) (query prom.Selector, found bool)
|
||||
// TODO: Don't house the external metric stuff side-by-side with the custom metric stuff.
|
||||
QueryForExternalMetric(info provider.ExternalMetricInfo, metricSelector labels.Selector) (query prom.Selector, found bool)
|
||||
// MatchValuesToNames matches result values to resource names for the given metric and value set
|
||||
MatchValuesToNames(metricInfo provider.CustomMetricInfo, values pmodel.Vector) (matchedValues map[string]pmodel.SampleValue, found bool)
|
||||
}
|
||||
|
|
@ -78,6 +75,7 @@ type basicSeriesRegistry struct {
|
|||
metricLister MetricListerWithNotification
|
||||
}
|
||||
|
||||
//NewBasicSeriesRegistry creates a SeriesRegistry driven by the data from the provided MetricLister.
|
||||
func NewBasicSeriesRegistry(lister MetricListerWithNotification, mapper apimeta.RESTMapper) SeriesRegistry {
|
||||
var registry = basicSeriesRegistry{
|
||||
mapper: mapper,
|
||||
|
|
@ -93,7 +91,7 @@ func (r *basicSeriesRegistry) filterMetrics(result metricUpdateResult) metricUpd
|
|||
namers := make([]MetricNamer, 0)
|
||||
series := make([][]prom.Series, 0)
|
||||
|
||||
targetType := config.MetricType("Custom")
|
||||
targetType := config.Custom
|
||||
|
||||
for i, namer := range result.namers {
|
||||
if namer.MetricType() == targetType {
|
||||
|
|
@ -205,30 +203,6 @@ func (r *basicSeriesRegistry) QueryForMetric(metricInfo provider.CustomMetricInf
|
|||
return query, true
|
||||
}
|
||||
|
||||
func (r *basicSeriesRegistry) QueryForExternalMetric(metricInfo provider.ExternalMetricInfo, metricSelector labels.Selector) (query prom.Selector, found bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
//TODO: Implementation
|
||||
return "", false
|
||||
// info, infoFound := r.info[metricInfo]
|
||||
// if !infoFound {
|
||||
// //TODO: Weird that it switches between types here.
|
||||
// glog.V(10).Infof("metric %v not registered", metricInfo)
|
||||
// return "", false
|
||||
// }
|
||||
|
||||
// query, err := info.namer.QueryForExternalSeries(info.seriesName, metricSelector)
|
||||
// if err != nil {
|
||||
// //TODO: See what was being .String() and implement that for ExternalMetricInfo.
|
||||
// // errorVal := metricInfo.String()
|
||||
// errorVal := "something"
|
||||
// glog.Errorf("unable to construct query for metric %s: %v", errorVal, err)
|
||||
// return "", false
|
||||
// }
|
||||
|
||||
// return query, true
|
||||
}
|
||||
|
||||
func (r *basicSeriesRegistry) MatchValuesToNames(metricInfo provider.CustomMetricInfo, values pmodel.Vector) (matchedValues map[string]pmodel.SampleValue, found bool) {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue