Skip to content

Commit

Permalink
Merge pull request #151 from JorTurFer/expose-single-metric
Browse files Browse the repository at this point in the history
feat: add default implementation for providers
  • Loading branch information
k8s-ci-robot committed May 3, 2023
2 parents 2db26d6 + 609f709 commit c68a98e
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 74 deletions.
38 changes: 10 additions & 28 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
```
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
8 changes: 3 additions & 5 deletions pkg/apiserver/installer/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions pkg/provider/defaults/default_metric_providers.go
Original file line number Diff line number Diff line change
@@ -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: "externalmetrics",
},
}
}

type DefaultCustomMetricsProvider struct{}

func (cm DefaultCustomMetricsProvider) ListAllMetrics() []provider.CustomMetricInfo {
return []provider.CustomMetricInfo{
{
Metric: "custommetrics",
},
}
}
14 changes: 5 additions & 9 deletions pkg/provider/fake/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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{}
Expand Down
9 changes: 7 additions & 2 deletions pkg/provider/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
}

Expand Down
33 changes: 3 additions & 30 deletions test-adapter/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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()
Expand All @@ -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
}

0 comments on commit c68a98e

Please sign in to comment.