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 }