From d90012439cfe78ee9afd3e436e44b5a8362a53f5 Mon Sep 17 00:00:00 2001 From: Tony Compton Date: Fri, 29 Jun 2018 09:46:26 -0400 Subject: [PATCH] Intermediate progress. Working through some of the result conversions. I want to use this code in a harness project to exercise some of it, but there are some versioning issues around my modified code vs. the current version of the real project. The easiest way to work around them at the moment is to push this. --- pkg/custom-provider/external_provider.go | 6 ++- .../metric-converter/matrix_converter.go | 2 +- .../metric-converter/metric_converter.go | 10 ++--- .../metric-converter/query_metadata.go | 11 ++++++ .../metric-converter/scalar_converter.go | 37 +++++++----------- .../metric-converter/vector_converter.go | 39 ++++++++++++++++--- 6 files changed, 68 insertions(+), 37 deletions(-) create mode 100644 pkg/custom-provider/metric-converter/query_metadata.go diff --git a/pkg/custom-provider/external_provider.go b/pkg/custom-provider/external_provider.go index bf9b5d62..58abb95a 100644 --- a/pkg/custom-provider/external_provider.go +++ b/pkg/custom-provider/external_provider.go @@ -78,7 +78,11 @@ func (p *externalPrometheusProvider) GetExternalMetric(namespace string, metricN return nil, err } - return p.metricConverter.Convert(queryResults) + queryMetadata := conv.QueryMetadata{ + MetricName: metricName, + WindowInSeconds: 0, + } + return p.metricConverter.Convert(queryMetadata, queryResults) } func (p *externalPrometheusProvider) ListAllExternalMetrics() []provider.ExternalMetricInfo { diff --git a/pkg/custom-provider/metric-converter/matrix_converter.go b/pkg/custom-provider/metric-converter/matrix_converter.go index 5db27e83..9b0be424 100644 --- a/pkg/custom-provider/metric-converter/matrix_converter.go +++ b/pkg/custom-provider/metric-converter/matrix_converter.go @@ -17,7 +17,7 @@ func NewMatrixConverter() MetricConverter { return &matrixConverter{} } -func (c *matrixConverter) Convert(queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { +func (c *matrixConverter) Convert(metadata QueryMetadata, queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { if queryResult.Type != model.ValMatrix { return nil, errors.New("matrixConverter can only convert scalar query results") } diff --git a/pkg/custom-provider/metric-converter/metric_converter.go b/pkg/custom-provider/metric-converter/metric_converter.go index fcbdf00f..a09f680b 100644 --- a/pkg/custom-provider/metric-converter/metric_converter.go +++ b/pkg/custom-provider/metric-converter/metric_converter.go @@ -11,7 +11,7 @@ import ( //MetricConverter provides a unified interface for converting the results of //Prometheus queries into external metric types. type MetricConverter interface { - Convert(queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) + Convert(metadata QueryMetadata, queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) } type metricConverter struct { @@ -30,17 +30,17 @@ func NewMetricConverter(scalar MetricConverter, vector MetricConverter, matrix M } } -func (c *metricConverter) Convert(queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { +func (c *metricConverter) Convert(metadata QueryMetadata, queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { if queryResult.Type == model.ValScalar { - return c.scalarConverter.Convert(queryResult) + return c.scalarConverter.Convert(metadata, queryResult) } if queryResult.Type == model.ValVector { - return c.vectorConverter.Convert(queryResult) + return c.vectorConverter.Convert(metadata, queryResult) } if queryResult.Type == model.ValMatrix { - return c.matrixConverter.Convert(queryResult) + return c.matrixConverter.Convert(metadata, queryResult) } return nil, errors.New("encountered an unexpected query result type") diff --git a/pkg/custom-provider/metric-converter/query_metadata.go b/pkg/custom-provider/metric-converter/query_metadata.go new file mode 100644 index 00000000..f8ef28e9 --- /dev/null +++ b/pkg/custom-provider/metric-converter/query_metadata.go @@ -0,0 +1,11 @@ +package provider + +//QueryMetadata is a data object the holds information about what inputs +//were used to generate Prometheus query results. In most cases it's not +//necessary, as the Prometheus result come back with enough information +//to determine the metric name. However, for scalar results, Prometheus +//only provides the value. +type QueryMetadata struct { + MetricName string + WindowInSeconds int64 +} diff --git a/pkg/custom-provider/metric-converter/scalar_converter.go b/pkg/custom-provider/metric-converter/scalar_converter.go index 29db3684..68a030e3 100644 --- a/pkg/custom-provider/metric-converter/scalar_converter.go +++ b/pkg/custom-provider/metric-converter/scalar_converter.go @@ -19,7 +19,7 @@ func NewScalarConverter() MetricConverter { return &scalarConverter{} } -func (c *scalarConverter) Convert(queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { +func (c *scalarConverter) Convert(metadata QueryMetadata, queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { if queryResult.Type != model.ValScalar { return nil, errors.New("scalarConverter can only convert scalar query results") } @@ -30,37 +30,26 @@ func (c *scalarConverter) Convert(queryResult prom.QueryResult) (*external_metri return nil, errors.New("the provided input did not contain scalar query results") } - return c.convert(toConvert) + return c.convert(metadata, toConvert) } -func (c *scalarConverter) convert(input *model.Scalar) (*external_metrics.ExternalMetricValueList, error) { - tempWindow := int64(0) +func (c *scalarConverter) convert(metadata QueryMetadata, input *model.Scalar) (*external_metrics.ExternalMetricValueList, error) { result := external_metrics.ExternalMetricValueList{ - //TODO: Where should all of these values come from? - TypeMeta: metav1.TypeMeta{ - Kind: "?", - APIVersion: "?", - }, - ListMeta: metav1.ListMeta{ - SelfLink: "?", - ResourceVersion: "?", - Continue: "?", - }, + //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{ external_metrics.ExternalMetricValue{ - TypeMeta: metav1.TypeMeta{ - Kind: "?", - APIVersion: "?", - }, - //TODO: Carry forward the metric name so we can set it here. - MetricName: "?", + MetricName: metadata.MetricName, Timestamp: metav1.Time{ input.Timestamp.Time(), }, - //TODO: Carry forward some information about our configuration so we can provide it here. - WindowSeconds: &tempWindow, - //TODO: Jump through the necessary hoops to convert our number into the proper type. - Value: resource.Quantity{}, + WindowSeconds: &metadata.WindowInSeconds, + //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.NewQuantity(int64(input.Value), resource.DecimalSI), }, }, } diff --git a/pkg/custom-provider/metric-converter/vector_converter.go b/pkg/custom-provider/metric-converter/vector_converter.go index 8ed6afb4..28a88268 100644 --- a/pkg/custom-provider/metric-converter/vector_converter.go +++ b/pkg/custom-provider/metric-converter/vector_converter.go @@ -5,6 +5,8 @@ import ( prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" "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" ) @@ -17,21 +19,46 @@ func NewVectorConverter() MetricConverter { return &vectorConverter{} } -func (c *vectorConverter) Convert(queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { +func (c *vectorConverter) Convert(metadata QueryMetadata, queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { if queryResult.Type != model.ValVector { return nil, errors.New("vectorConverter can only convert scalar query results") } - toConvert := queryResult.Vector + toConvert := *queryResult.Vector if toConvert == nil { return nil, errors.New("the provided input did not contain vector query results") } - return c.convert(toConvert) + return c.convert(metadata, toConvert) } -func (c *vectorConverter) convert(result *model.Vector) (*external_metrics.ExternalMetricValueList, error) { - //TODO: Implementation. - return nil, nil +func (c *vectorConverter) convert(metadata QueryMetadata, result model.Vector) (*external_metrics.ExternalMetricValueList, error) { + items := []external_metrics.ExternalMetricValue{} + metricValueList := external_metrics.ExternalMetricValueList{ + Items: items, + } + + numSamples := result.Len() + if numSamples == 0 { + return &metricValueList, nil + } + + for _, val := range result { + singleMetric := external_metrics.ExternalMetricValue{ + MetricName: string(val.Metric[model.LabelName("__name__")]), + Timestamp: metav1.Time{ + 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) + } + + return &metricValueList, nil }