From 4402792fb081da12b2716ab4175a10ffc7ec26f0 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Fri, 28 Apr 2023 22:20:42 +0200 Subject: [PATCH] feat: add default implementation for providers Signed-off-by: Jorge Turrado --- docs/getting-started.md | 38 +++++------------ pkg/apiserver/installer/apiserver_test.go | 8 ++-- .../defaults/default_metric_providers.go | 42 +++++++++++++++++++ pkg/provider/fake/fake.go | 14 +++---- pkg/provider/interfaces.go | 9 +++- test-adapter/provider/provider.go | 33 ++------------- 6 files changed, 70 insertions(+), 74 deletions(-) create mode 100644 pkg/provider/defaults/default_metric_providers.go diff --git a/docs/getting-started.md b/docs/getting-started.md index 43d3073d..ea08a498 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -56,6 +56,7 @@ import ( "k8s.io/metrics/pkg/apis/custom_metrics" "sigs.k8s.io/custom-metrics-apiserver/pkg/provider" + "sigs.k8s.io/custom-metrics-apiserver/pkg/provider/defaults" "sigs.k8s.io/custom-metrics-apiserver/pkg/provider/helpers" ) ``` @@ -76,35 +77,14 @@ type CustomMetricsProvider interface { First, there's a method for listing all metrics available at any point in time. It's used to populate the discovery information in the API, so that clients can know what metrics are available. It's not allowed to fail (it -doesn't return any error), and it should return quickly, so it's suggested -that you update it asynchronously in real-world code. +doesn't return any error), and it should return quickly. -For this walkthrough, you can just return a few statically-named metrics, -two that are namespaced, and one that's on namespaces themselves, and thus -root-scoped: - -```go -func (p *yourProvider) ListAllMetrics() []provider.CustomMetricInfo { - return []provider.CustomMetricInfo{ - // these are mostly arbitrary examples - { - GroupResource: schema.GroupResource{Group: "", Resource: "pods"}, - Metric: "packets-per-second", - Namespaced: true, - }, - { - GroupResource: schema.GroupResource{Group: "", Resource: "services"}, - Metric: "connections-per-second", - Namespaced: true, - }, - { - GroupResource: schema.GroupResource{Group: "", Resource: "namespaces"}, - Metric: "work-queue-length", - Namespaced: false, - }, - } -} -``` +You can list your metrics (asynchronously) and return them on every request. +This is not mandatory because kubernetes can request metric values without +listing them before, but maybe there are some cases where is useful. To +provide a unified solution, a default implementation is provided thanks to +`DefaultCustomMetricsProvider` (and `DefaultExternalMetricsProvider` for +external metrics) Next, you'll need to implement the methods that actually fetch the metrics. There are methods for fetching metrics describing arbitrary Kubernetes @@ -189,6 +169,8 @@ already have sufficient information in your metrics pipeline: ```go type yourProvider struct { + defaults.DefaultCustomMetricsProvider + defaults.DefaultExternalMetricsProvider client dynamic.Interface mapper apimeta.RESTMapper diff --git a/pkg/apiserver/installer/apiserver_test.go b/pkg/apiserver/installer/apiserver_test.go index 8936871f..be4d2b4c 100644 --- a/pkg/apiserver/installer/apiserver_test.go +++ b/pkg/apiserver/installer/apiserver_test.go @@ -45,6 +45,7 @@ import ( emv1beta1 "k8s.io/metrics/pkg/apis/external_metrics/v1beta1" "sigs.k8s.io/custom-metrics-apiserver/pkg/provider" + "sigs.k8s.io/custom-metrics-apiserver/pkg/provider/defaults" custommetricstorage "sigs.k8s.io/custom-metrics-apiserver/pkg/registry/custom_metrics" externalmetricstorage "sigs.k8s.io/custom-metrics-apiserver/pkg/registry/external_metrics" sampleprovider "sigs.k8s.io/custom-metrics-apiserver/test-adapter/provider" @@ -179,7 +180,8 @@ type fakeCMProvider struct { namespacedValues map[string][]custom_metrics.MetricValue rootSubsetCounts map[string]int namespacedSubsetCounts map[string]int - metrics []provider.CustomMetricInfo + + defaults.DefaultCustomMetricsProvider } func (p *fakeCMProvider) valuesFor(name types.NamespacedName, info provider.CustomMetricInfo) (string, []custom_metrics.MetricValue, bool) { @@ -241,10 +243,6 @@ func (p *fakeCMProvider) GetMetricBySelector(_ context.Context, namespace string return &trimmedValues, nil } -func (p *fakeCMProvider) ListAllMetrics() []provider.CustomMetricInfo { - return p.metrics -} - type T struct { Method string Path string diff --git a/pkg/provider/defaults/default_metric_providers.go b/pkg/provider/defaults/default_metric_providers.go new file mode 100644 index 00000000..5d06bfea --- /dev/null +++ b/pkg/provider/defaults/default_metric_providers.go @@ -0,0 +1,42 @@ +/* +Copyright 2023 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 defaults provides a default implementation of metrics providers. +package defaults + +import ( + "sigs.k8s.io/custom-metrics-apiserver/pkg/provider" +) + +type DefaultExternalMetricsProvider struct{} + +func (em DefaultExternalMetricsProvider) ListAllExternalMetrics() []provider.ExternalMetricInfo { + return []provider.ExternalMetricInfo{ + { + Metric: "externalmetric", + }, + } +} + +type DefaultCustomMetricsProvider struct{} + +func (cm DefaultCustomMetricsProvider) ListAllMetrics() []provider.CustomMetricInfo { + return []provider.CustomMetricInfo{ + { + Metric: "custommetric", + }, + } +} diff --git a/pkg/provider/fake/fake.go b/pkg/provider/fake/fake.go index 296d29fb..b76c8a13 100644 --- a/pkg/provider/fake/fake.go +++ b/pkg/provider/fake/fake.go @@ -26,9 +26,13 @@ import ( "k8s.io/metrics/pkg/apis/external_metrics" "sigs.k8s.io/custom-metrics-apiserver/pkg/provider" + "sigs.k8s.io/custom-metrics-apiserver/pkg/provider/defaults" ) -type fakeProvider struct{} +type fakeProvider struct { + defaults.DefaultCustomMetricsProvider + defaults.DefaultExternalMetricsProvider +} func (*fakeProvider) GetMetricByName(_ context.Context, _ types.NamespacedName, _ provider.CustomMetricInfo, _ labels.Selector) (*custom_metrics.MetricValue, error) { return &custom_metrics.MetricValue{}, nil @@ -38,18 +42,10 @@ func (*fakeProvider) GetMetricBySelector(_ context.Context, _ string, _ labels.S return &custom_metrics.MetricValueList{}, nil } -func (*fakeProvider) ListAllMetrics() []provider.CustomMetricInfo { - return []provider.CustomMetricInfo{} -} - func (*fakeProvider) GetExternalMetric(_ context.Context, _ string, _ labels.Selector, _ provider.ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error) { return &external_metrics.ExternalMetricValueList{}, nil } -func (*fakeProvider) ListAllExternalMetrics() []provider.ExternalMetricInfo { - return []provider.ExternalMetricInfo{} -} - // NewProvider creates a fake implementation of MetricsProvider. func NewProvider() provider.MetricsProvider { return &fakeProvider{} diff --git a/pkg/provider/interfaces.go b/pkg/provider/interfaces.go index 310eb0c7..8f26ded6 100644 --- a/pkg/provider/interfaces.go +++ b/pkg/provider/interfaces.go @@ -92,8 +92,8 @@ type CustomMetricsProvider interface { // ListAllMetrics provides a list of all available metrics at // the current time. Note that this is not allowed to return - // an error, so it is recommended that implementors cache and - // periodically update this list, instead of querying every time. + // an error, so it is recommended that implementors use the + // default implementation provided by DefaultCustomMetricsProvider. ListAllMetrics() []CustomMetricInfo } @@ -104,6 +104,11 @@ type CustomMetricsProvider interface { type ExternalMetricsProvider interface { GetExternalMetric(ctx context.Context, namespace string, metricSelector labels.Selector, info ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error) + // ListAllExternalMetrics provides a list of all available + // external metrics at the current time. + // Note that this is not allowed to return an error, so it is + // recommended that implementors use the default implementation + // provided by DefaultExternalMetricsProvider. ListAllExternalMetrics() []ExternalMetricInfo } diff --git a/test-adapter/provider/provider.go b/test-adapter/provider/provider.go index 6f09d98e..9ec67a84 100644 --- a/test-adapter/provider/provider.go +++ b/test-adapter/provider/provider.go @@ -35,6 +35,7 @@ import ( "k8s.io/metrics/pkg/apis/external_metrics" "sigs.k8s.io/custom-metrics-apiserver/pkg/provider" + "sigs.k8s.io/custom-metrics-apiserver/pkg/provider/defaults" "sigs.k8s.io/custom-metrics-apiserver/pkg/provider/helpers" ) @@ -105,6 +106,8 @@ var _ provider.MetricsProvider = &testingProvider{} // testingProvider is a sample implementation of provider.MetricsProvider which stores a map of fake metrics type testingProvider struct { + defaults.DefaultCustomMetricsProvider + defaults.DefaultExternalMetricsProvider client dynamic.Interface mapper apimeta.RESTMapper @@ -308,25 +311,6 @@ func (p *testingProvider) GetMetricBySelector(_ context.Context, namespace strin return p.metricsFor(namespace, selector, info, metricSelector) } -func (p *testingProvider) ListAllMetrics() []provider.CustomMetricInfo { - p.valuesLock.RLock() - defer p.valuesLock.RUnlock() - - // Get unique CustomMetricInfos from wrapper CustomMetricResources - infos := make(map[provider.CustomMetricInfo]struct{}) - for resource := range p.values { - infos[resource.CustomMetricInfo] = struct{}{} - } - - // Build slice of CustomMetricInfos to be returns - metrics := make([]provider.CustomMetricInfo, 0, len(infos)) - for info := range infos { - metrics = append(metrics, info) - } - - return metrics -} - func (p *testingProvider) GetExternalMetric(_ context.Context, _ string, metricSelector labels.Selector, info provider.ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error) { p.valuesLock.RLock() defer p.valuesLock.RUnlock() @@ -344,14 +328,3 @@ func (p *testingProvider) GetExternalMetric(_ context.Context, _ string, metricS Items: matchingMetrics, }, nil } - -func (p *testingProvider) ListAllExternalMetrics() []provider.ExternalMetricInfo { - p.valuesLock.RLock() - defer p.valuesLock.RUnlock() - - externalMetricsInfo := []provider.ExternalMetricInfo{} - for _, metric := range p.externalMetrics { - externalMetricsInfo = append(externalMetricsInfo, metric.info) - } - return externalMetricsInfo -}