Skip to content

Commit c68a98e

Browse files
authored
Merge pull request #151 from JorTurFer/expose-single-metric
feat: add default implementation for providers
2 parents 2db26d6 + 609f709 commit c68a98e

File tree

6 files changed

+70
-74
lines changed

6 files changed

+70
-74
lines changed

docs/getting-started.md

+10-28
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import (
5656
"k8s.io/metrics/pkg/apis/custom_metrics"
5757

5858
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider"
59+
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider/defaults"
5960
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider/helpers"
6061
)
6162
```
@@ -76,35 +77,14 @@ type CustomMetricsProvider interface {
7677
First, there's a method for listing all metrics available at any point in
7778
time. It's used to populate the discovery information in the API, so that
7879
clients can know what metrics are available. It's not allowed to fail (it
79-
doesn't return any error), and it should return quickly, so it's suggested
80-
that you update it asynchronously in real-world code.
80+
doesn't return any error), and it should return quickly.
8181

82-
For this walkthrough, you can just return a few statically-named metrics,
83-
two that are namespaced, and one that's on namespaces themselves, and thus
84-
root-scoped:
85-
86-
```go
87-
func (p *yourProvider) ListAllMetrics() []provider.CustomMetricInfo {
88-
return []provider.CustomMetricInfo{
89-
// these are mostly arbitrary examples
90-
{
91-
GroupResource: schema.GroupResource{Group: "", Resource: "pods"},
92-
Metric: "packets-per-second",
93-
Namespaced: true,
94-
},
95-
{
96-
GroupResource: schema.GroupResource{Group: "", Resource: "services"},
97-
Metric: "connections-per-second",
98-
Namespaced: true,
99-
},
100-
{
101-
GroupResource: schema.GroupResource{Group: "", Resource: "namespaces"},
102-
Metric: "work-queue-length",
103-
Namespaced: false,
104-
},
105-
}
106-
}
107-
```
82+
You can list your metrics (asynchronously) and return them on every request.
83+
This is not mandatory because kubernetes can request metric values without
84+
listing them before, but maybe there are some cases where is useful. To
85+
provide a unified solution, a default implementation is provided thanks to
86+
`DefaultCustomMetricsProvider` (and `DefaultExternalMetricsProvider` for
87+
external metrics)
10888

10989
Next, you'll need to implement the methods that actually fetch the
11090
metrics. There are methods for fetching metrics describing arbitrary Kubernetes
@@ -189,6 +169,8 @@ already have sufficient information in your metrics pipeline:
189169

190170
```go
191171
type yourProvider struct {
172+
defaults.DefaultCustomMetricsProvider
173+
defaults.DefaultExternalMetricsProvider
192174
client dynamic.Interface
193175
mapper apimeta.RESTMapper
194176

pkg/apiserver/installer/apiserver_test.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
emv1beta1 "k8s.io/metrics/pkg/apis/external_metrics/v1beta1"
4646

4747
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider"
48+
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider/defaults"
4849
custommetricstorage "sigs.k8s.io/custom-metrics-apiserver/pkg/registry/custom_metrics"
4950
externalmetricstorage "sigs.k8s.io/custom-metrics-apiserver/pkg/registry/external_metrics"
5051
sampleprovider "sigs.k8s.io/custom-metrics-apiserver/test-adapter/provider"
@@ -179,7 +180,8 @@ type fakeCMProvider struct {
179180
namespacedValues map[string][]custom_metrics.MetricValue
180181
rootSubsetCounts map[string]int
181182
namespacedSubsetCounts map[string]int
182-
metrics []provider.CustomMetricInfo
183+
184+
defaults.DefaultCustomMetricsProvider
183185
}
184186

185187
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
241243
return &trimmedValues, nil
242244
}
243245

244-
func (p *fakeCMProvider) ListAllMetrics() []provider.CustomMetricInfo {
245-
return p.metrics
246-
}
247-
248246
type T struct {
249247
Method string
250248
Path string
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package defaults provides a default implementation of metrics providers.
18+
package defaults
19+
20+
import (
21+
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider"
22+
)
23+
24+
type DefaultExternalMetricsProvider struct{}
25+
26+
func (em DefaultExternalMetricsProvider) ListAllExternalMetrics() []provider.ExternalMetricInfo {
27+
return []provider.ExternalMetricInfo{
28+
{
29+
Metric: "externalmetrics",
30+
},
31+
}
32+
}
33+
34+
type DefaultCustomMetricsProvider struct{}
35+
36+
func (cm DefaultCustomMetricsProvider) ListAllMetrics() []provider.CustomMetricInfo {
37+
return []provider.CustomMetricInfo{
38+
{
39+
Metric: "custommetrics",
40+
},
41+
}
42+
}

pkg/provider/fake/fake.go

+5-9
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@ import (
2626
"k8s.io/metrics/pkg/apis/external_metrics"
2727

2828
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider"
29+
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider/defaults"
2930
)
3031

31-
type fakeProvider struct{}
32+
type fakeProvider struct {
33+
defaults.DefaultCustomMetricsProvider
34+
defaults.DefaultExternalMetricsProvider
35+
}
3236

3337
func (*fakeProvider) GetMetricByName(_ context.Context, _ types.NamespacedName, _ provider.CustomMetricInfo, _ labels.Selector) (*custom_metrics.MetricValue, error) {
3438
return &custom_metrics.MetricValue{}, nil
@@ -38,18 +42,10 @@ func (*fakeProvider) GetMetricBySelector(_ context.Context, _ string, _ labels.S
3842
return &custom_metrics.MetricValueList{}, nil
3943
}
4044

41-
func (*fakeProvider) ListAllMetrics() []provider.CustomMetricInfo {
42-
return []provider.CustomMetricInfo{}
43-
}
44-
4545
func (*fakeProvider) GetExternalMetric(_ context.Context, _ string, _ labels.Selector, _ provider.ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error) {
4646
return &external_metrics.ExternalMetricValueList{}, nil
4747
}
4848

49-
func (*fakeProvider) ListAllExternalMetrics() []provider.ExternalMetricInfo {
50-
return []provider.ExternalMetricInfo{}
51-
}
52-
5349
// NewProvider creates a fake implementation of MetricsProvider.
5450
func NewProvider() provider.MetricsProvider {
5551
return &fakeProvider{}

pkg/provider/interfaces.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ type CustomMetricsProvider interface {
9292

9393
// ListAllMetrics provides a list of all available metrics at
9494
// the current time. Note that this is not allowed to return
95-
// an error, so it is recommended that implementors cache and
96-
// periodically update this list, instead of querying every time.
95+
// an error, so it is recommended that implementors use the
96+
// default implementation provided by DefaultCustomMetricsProvider.
9797
ListAllMetrics() []CustomMetricInfo
9898
}
9999

@@ -104,6 +104,11 @@ type CustomMetricsProvider interface {
104104
type ExternalMetricsProvider interface {
105105
GetExternalMetric(ctx context.Context, namespace string, metricSelector labels.Selector, info ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error)
106106

107+
// ListAllExternalMetrics provides a list of all available
108+
// external metrics at the current time.
109+
// Note that this is not allowed to return an error, so it is
110+
// recommended that implementors use the default implementation
111+
// provided by DefaultExternalMetricsProvider.
107112
ListAllExternalMetrics() []ExternalMetricInfo
108113
}
109114

test-adapter/provider/provider.go

+3-30
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"k8s.io/metrics/pkg/apis/external_metrics"
3636

3737
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider"
38+
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider/defaults"
3839
"sigs.k8s.io/custom-metrics-apiserver/pkg/provider/helpers"
3940
)
4041

@@ -105,6 +106,8 @@ var _ provider.MetricsProvider = &testingProvider{}
105106

106107
// testingProvider is a sample implementation of provider.MetricsProvider which stores a map of fake metrics
107108
type testingProvider struct {
109+
defaults.DefaultCustomMetricsProvider
110+
defaults.DefaultExternalMetricsProvider
108111
client dynamic.Interface
109112
mapper apimeta.RESTMapper
110113

@@ -308,25 +311,6 @@ func (p *testingProvider) GetMetricBySelector(_ context.Context, namespace strin
308311
return p.metricsFor(namespace, selector, info, metricSelector)
309312
}
310313

311-
func (p *testingProvider) ListAllMetrics() []provider.CustomMetricInfo {
312-
p.valuesLock.RLock()
313-
defer p.valuesLock.RUnlock()
314-
315-
// Get unique CustomMetricInfos from wrapper CustomMetricResources
316-
infos := make(map[provider.CustomMetricInfo]struct{})
317-
for resource := range p.values {
318-
infos[resource.CustomMetricInfo] = struct{}{}
319-
}
320-
321-
// Build slice of CustomMetricInfos to be returns
322-
metrics := make([]provider.CustomMetricInfo, 0, len(infos))
323-
for info := range infos {
324-
metrics = append(metrics, info)
325-
}
326-
327-
return metrics
328-
}
329-
330314
func (p *testingProvider) GetExternalMetric(_ context.Context, _ string, metricSelector labels.Selector, info provider.ExternalMetricInfo) (*external_metrics.ExternalMetricValueList, error) {
331315
p.valuesLock.RLock()
332316
defer p.valuesLock.RUnlock()
@@ -344,14 +328,3 @@ func (p *testingProvider) GetExternalMetric(_ context.Context, _ string, metricS
344328
Items: matchingMetrics,
345329
}, nil
346330
}
347-
348-
func (p *testingProvider) ListAllExternalMetrics() []provider.ExternalMetricInfo {
349-
p.valuesLock.RLock()
350-
defer p.valuesLock.RUnlock()
351-
352-
externalMetricsInfo := []provider.ExternalMetricInfo{}
353-
for _, metric := range p.externalMetrics {
354-
externalMetricsInfo = append(externalMetricsInfo, metric.info)
355-
}
356-
return externalMetricsInfo
357-
}

0 commit comments

Comments
 (0)