From a69a5cbbcce5eb44b4982ec7aab0350027a6561c Mon Sep 17 00:00:00 2001 From: Tony Compton Date: Thu, 28 Jun 2018 22:08:21 -0400 Subject: [PATCH] Stubbing out metric converters. --- pkg/custom-provider/external_provider.go | 54 +++++++-------- .../metric-converter/matrix_converter.go | 37 ++++++++++ .../metric-converter/metric_converter.go | 47 +++++++++++++ .../metric-converter/scalar_converter.go | 68 +++++++++++++++++++ .../metric-converter/vector_converter.go | 37 ++++++++++ 5 files changed, 213 insertions(+), 30 deletions(-) create mode 100644 pkg/custom-provider/metric-converter/matrix_converter.go create mode 100644 pkg/custom-provider/metric-converter/metric_converter.go create mode 100644 pkg/custom-provider/metric-converter/scalar_converter.go create mode 100644 pkg/custom-provider/metric-converter/vector_converter.go diff --git a/pkg/custom-provider/external_provider.go b/pkg/custom-provider/external_provider.go index 5eaf10f3..bf9b5d62 100644 --- a/pkg/custom-provider/external_provider.go +++ b/pkg/custom-provider/external_provider.go @@ -1,29 +1,14 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - package provider import ( "context" - "fmt" "time" pmodel "github.com/prometheus/common/model" + conv "github.com/directxman12/k8s-prometheus-adapter/pkg/custom-provider/metric-converter" "github.com/kubernetes-incubator/custom-metrics-apiserver/pkg/provider" + apimeta "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/dynamic" @@ -32,16 +17,22 @@ import ( prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" ) +//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. type externalPrometheusProvider struct { - mapper apimeta.RESTMapper - kubeClient dynamic.Interface - promClient prom.Client - queryBuilder ExternalMetricQueryBuilder + mapper apimeta.RESTMapper + kubeClient dynamic.Interface + promClient prom.Client + queryBuilder ExternalMetricQueryBuilder + metricConverter conv.MetricConverter SeriesRegistry } @@ -52,7 +43,8 @@ type externalPrometheusProvider struct { //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. -func NewExternalPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.Interface, promClient prom.Client, namers []MetricNamer, updateInterval time.Duration, queryBuilder ExternalMetricQueryBuilder) (provider.ExternalMetricsProvider, Runnable) { + +func NewExternalPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic.Interface, promClient prom.Client, namers []MetricNamer, updateInterval time.Duration, queryBuilder ExternalMetricQueryBuilder, metricConverter conv.MetricConverter) (provider.ExternalMetricsProvider, Runnable) { lister := &cachingMetricsLister{ updateInterval: updateInterval, promClient: promClient, @@ -64,9 +56,10 @@ func NewExternalPrometheusProvider(mapper apimeta.RESTMapper, kubeClient dynamic } return &externalPrometheusProvider{ - mapper: mapper, - kubeClient: kubeClient, - promClient: promClient, + mapper: mapper, + kubeClient: kubeClient, + promClient: promClient, + metricConverter: metricConverter, SeriesRegistry: lister, }, lister @@ -79,12 +72,13 @@ func (p *externalPrometheusProvider) GetExternalMetric(namespace string, metricN //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) - //TODO: Only here to stop compiler issues in this incomplete code. - fmt.Printf("%s, %s", queryResults, err) + 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 + } - //TODO: Check for errors. See what the custromPrometheusProvider does for errors. - //TODO: Adapt the results in queryResults to the appropriate return type. - return nil, nil + return p.metricConverter.Convert(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 new file mode 100644 index 00000000..5db27e83 --- /dev/null +++ b/pkg/custom-provider/metric-converter/matrix_converter.go @@ -0,0 +1,37 @@ +package provider + +import ( + "errors" + + prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" + "github.com/prometheus/common/model" + "k8s.io/metrics/pkg/apis/external_metrics" +) + +type matrixConverter struct { +} + +//NewMatrixConverter creates a MatrixConverter capable of converting +//matrix Prometheus query results into external metric types. +func NewMatrixConverter() MetricConverter { + return &matrixConverter{} +} + +func (c *matrixConverter) Convert(queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { + if queryResult.Type != model.ValMatrix { + return nil, errors.New("matrixConverter can only convert scalar query results") + } + + toConvert := queryResult.Matrix + + if toConvert == nil { + return nil, errors.New("the provided input did not contain matrix query results") + } + + return c.convert(toConvert) +} + +func (c *matrixConverter) convert(result *model.Matrix) (*external_metrics.ExternalMetricValueList, error) { + //TODO: Implementation. + return nil, nil +} diff --git a/pkg/custom-provider/metric-converter/metric_converter.go b/pkg/custom-provider/metric-converter/metric_converter.go new file mode 100644 index 00000000..fcbdf00f --- /dev/null +++ b/pkg/custom-provider/metric-converter/metric_converter.go @@ -0,0 +1,47 @@ +package provider + +import ( + "errors" + + prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" + "github.com/prometheus/common/model" + "k8s.io/metrics/pkg/apis/external_metrics" +) + +//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) +} + +type metricConverter struct { + scalarConverter MetricConverter + vectorConverter MetricConverter + matrixConverter MetricConverter +} + +//NewMetricConverter creates a MetricCoverter, capable of converting any of the three metric types +//returned by the Prometheus client into external metrics types. +func NewMetricConverter(scalar MetricConverter, vector MetricConverter, matrix MetricConverter) MetricConverter { + return &metricConverter{ + scalarConverter: scalar, + vectorConverter: vector, + matrixConverter: matrix, + } +} + +func (c *metricConverter) Convert(queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { + if queryResult.Type == model.ValScalar { + return c.scalarConverter.Convert(queryResult) + } + + if queryResult.Type == model.ValVector { + return c.vectorConverter.Convert(queryResult) + } + + if queryResult.Type == model.ValMatrix { + return c.matrixConverter.Convert(queryResult) + } + + return nil, errors.New("encountered an unexpected query result type") +} diff --git a/pkg/custom-provider/metric-converter/scalar_converter.go b/pkg/custom-provider/metric-converter/scalar_converter.go new file mode 100644 index 00000000..29db3684 --- /dev/null +++ b/pkg/custom-provider/metric-converter/scalar_converter.go @@ -0,0 +1,68 @@ +package provider + +import ( + "errors" + + 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" +) + +type scalarConverter struct { +} + +//NewScalarConverter creates a ScalarConverter capable of converting +//scalar Prometheus query results into external metric types. +func NewScalarConverter() MetricConverter { + return &scalarConverter{} +} + +func (c *scalarConverter) Convert(queryResult prom.QueryResult) (*external_metrics.ExternalMetricValueList, error) { + if queryResult.Type != model.ValScalar { + return nil, errors.New("scalarConverter can only convert scalar query results") + } + + toConvert := queryResult.Scalar + + if toConvert == nil { + return nil, errors.New("the provided input did not contain scalar query results") + } + + return c.convert(toConvert) +} + +func (c *scalarConverter) convert(input *model.Scalar) (*external_metrics.ExternalMetricValueList, error) { + tempWindow := int64(0) + result := external_metrics.ExternalMetricValueList{ + //TODO: Where should all of these values come from? + TypeMeta: metav1.TypeMeta{ + Kind: "?", + APIVersion: "?", + }, + ListMeta: metav1.ListMeta{ + SelfLink: "?", + ResourceVersion: "?", + Continue: "?", + }, + 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: "?", + 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{}, + }, + }, + } + return &result, nil +} diff --git a/pkg/custom-provider/metric-converter/vector_converter.go b/pkg/custom-provider/metric-converter/vector_converter.go new file mode 100644 index 00000000..8ed6afb4 --- /dev/null +++ b/pkg/custom-provider/metric-converter/vector_converter.go @@ -0,0 +1,37 @@ +package provider + +import ( + "errors" + + prom "github.com/directxman12/k8s-prometheus-adapter/pkg/client" + "github.com/prometheus/common/model" + "k8s.io/metrics/pkg/apis/external_metrics" +) + +type vectorConverter struct { +} + +//NewVectorConverter creates a VectorConverter capable of converting +//vector Prometheus query results into external metric types. +func NewVectorConverter() MetricConverter { + return &vectorConverter{} +} + +func (c *vectorConverter) Convert(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 + + if toConvert == nil { + return nil, errors.New("the provided input did not contain vector query results") + } + + return c.convert(toConvert) +} + +func (c *vectorConverter) convert(result *model.Vector) (*external_metrics.ExternalMetricValueList, error) { + //TODO: Implementation. + return nil, nil +}